blob: a4add6a47c751698aa0705a434c4d7cd309a24d3 [file] [log] [blame]
"Robert P. J. Day"63fc1a92006-07-02 19:47:05 +00001/* vi: set sw=4 ts=4: */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00002/*
3 * ipaddress.c "ip address".
4 *
Bernhard Reutner-Fischer20f40002006-01-30 17:17:14 +00005 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00006 *
7 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
8 *
9 * Changes:
10 * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
11 */
12
Denis Vlasenko9a7d38f2007-05-31 22:42:12 +000013//#include <sys/socket.h>
14//#include <sys/ioctl.h>
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000015#include <fnmatch.h>
Eric Andersen8004bb72003-01-14 08:06:07 +000016#include <net/if.h>
17#include <net/if_arp.h>
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000018
Denis Vlasenko9a7d38f2007-05-31 22:42:12 +000019#include "ip_common.h" /* #include "libbb.h" is inside */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000020#include "rt_names.h"
21#include "utils.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000022
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000023
Denis Vlasenko540a2a12007-04-07 01:14:45 +000024typedef struct filter_t {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000025 int ifindex;
26 int family;
27 int oneline;
28 int showqueue;
29 inet_prefix pfx;
30 int scope, scopemask;
31 int flags, flagmask;
32 int up;
33 char *label;
Glenn L McGrathd66370c2003-01-13 21:40:38 +000034 int flushed;
35 char *flushb;
36 int flushp;
37 int flushe;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000038 struct rtnl_handle *rth;
Denis Vlasenko540a2a12007-04-07 01:14:45 +000039} filter_t;
40
41#define filter (*(filter_t*)&bb_common_bufsiz1)
42
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000043
Eric Andersen14f5c8d2005-04-16 19:39:00 +000044static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000045{
46 fprintf(fp, "<");
47 flags &= ~IFF_RUNNING;
48#define _PF(f) if (flags&IFF_##f) { \
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000049 flags &= ~IFF_##f ; \
50 fprintf(fp, #f "%s", flags ? "," : ""); }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000051 _PF(LOOPBACK);
52 _PF(BROADCAST);
53 _PF(POINTOPOINT);
54 _PF(MULTICAST);
55 _PF(NOARP);
56#if 0
57 _PF(ALLMULTI);
58 _PF(PROMISC);
59 _PF(MASTER);
60 _PF(SLAVE);
61 _PF(DEBUG);
62 _PF(DYNAMIC);
63 _PF(AUTOMEDIA);
64 _PF(PORTSEL);
65 _PF(NOTRAILERS);
66#endif
67 _PF(UP);
68#undef _PF
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000069 if (flags)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000070 fprintf(fp, "%x", flags);
71 if (mdown)
72 fprintf(fp, ",M-DOWN");
73 fprintf(fp, "> ");
74}
75
Glenn L McGrath2626ef62002-12-02 01:40:05 +000076static void print_queuelen(char *name)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000077{
78 struct ifreq ifr;
79 int s;
80
81 s = socket(AF_INET, SOCK_STREAM, 0);
82 if (s < 0)
83 return;
84
85 memset(&ifr, 0, sizeof(ifr));
Denis Vlasenko229b3d22006-11-27 23:44:57 +000086 strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
Eric Andersenc7bda1c2004-03-15 08:29:22 +000087 if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +000088 bb_perror_msg("SIOCGIFXQLEN");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000089 close(s);
90 return;
91 }
92 close(s);
93
94 if (ifr.ifr_qlen)
95 printf("qlen %d", ifr.ifr_qlen);
96}
97
Bernhard Reutner-Fischer20f40002006-01-30 17:17:14 +000098static int print_linkinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who,
Bernhard Reutner-Fischer921f5df2006-11-21 15:36:08 +000099 const struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000100{
101 FILE *fp = (FILE*)arg;
102 struct ifinfomsg *ifi = NLMSG_DATA(n);
103 struct rtattr * tb[IFLA_MAX+1];
104 int len = n->nlmsg_len;
105 unsigned m_flag = 0;
106
107 if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
108 return 0;
109
110 len -= NLMSG_LENGTH(sizeof(*ifi));
111 if (len < 0)
112 return -1;
113
114 if (filter.ifindex && ifi->ifi_index != filter.ifindex)
115 return 0;
116 if (filter.up && !(ifi->ifi_flags&IFF_UP))
117 return 0;
118
119 memset(tb, 0, sizeof(tb));
120 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
121 if (tb[IFLA_IFNAME] == NULL) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000122 bb_error_msg("nil ifname");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000123 return -1;
124 }
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000125 if (filter.label
126 && (!filter.family || filter.family == AF_PACKET)
127 && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)
128 ) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000129 return 0;
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000130 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000131
132 if (n->nlmsg_type == RTM_DELLINK)
133 fprintf(fp, "Deleted ");
134
135 fprintf(fp, "%d: %s", ifi->ifi_index,
136 tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
137
138 if (tb[IFLA_LINK]) {
139 SPRINT_BUF(b1);
140 int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
141 if (iflink == 0)
142 fprintf(fp, "@NONE: ");
143 else {
144 fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
145 m_flag = ll_index_to_flags(iflink);
146 m_flag = !(m_flag & IFF_UP);
147 }
148 } else {
149 fprintf(fp, ": ");
150 }
151 print_link_flags(fp, ifi->ifi_flags, m_flag);
152
153 if (tb[IFLA_MTU])
154 fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
155 if (tb[IFLA_QDISC])
156 fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
157#ifdef IFLA_MASTER
158 if (tb[IFLA_MASTER]) {
159 SPRINT_BUF(b1);
160 fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
161 }
162#endif
163 if (filter.showqueue)
164 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000165
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000166 if (!filter.family || filter.family == AF_PACKET) {
167 SPRINT_BUF(b1);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000168 fprintf(fp, "%c link/%s ", _SL_, ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000169
170 if (tb[IFLA_ADDRESS]) {
171 fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
172 RTA_PAYLOAD(tb[IFLA_ADDRESS]),
173 ifi->ifi_type,
174 b1, sizeof(b1)));
175 }
176 if (tb[IFLA_BROADCAST]) {
177 if (ifi->ifi_flags&IFF_POINTOPOINT)
178 fprintf(fp, " peer ");
179 else
180 fprintf(fp, " brd ");
181 fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
182 RTA_PAYLOAD(tb[IFLA_BROADCAST]),
183 ifi->ifi_type,
184 b1, sizeof(b1)));
185 }
186 }
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000187 fputc('\n', fp);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000188 fflush(fp);
189 return 0;
190}
191
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000192static int flush_update(void)
193{
194 if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000195 bb_perror_msg("failed to send flush request");
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000196 return -1;
197 }
198 filter.flushp = 0;
199 return 0;
200}
201
Bernhard Reutner-Fischer20f40002006-01-30 17:17:14 +0000202static int print_addrinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who,
203 struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000204{
205 FILE *fp = (FILE*)arg;
206 struct ifaddrmsg *ifa = NLMSG_DATA(n);
207 int len = n->nlmsg_len;
208 struct rtattr * rta_tb[IFA_MAX+1];
209 char abuf[256];
210 SPRINT_BUF(b1);
211
212 if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
213 return 0;
214 len -= NLMSG_LENGTH(sizeof(*ifa));
215 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000216 bb_error_msg("wrong nlmsg len %d", len);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000217 return -1;
218 }
219
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000220 if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
221 return 0;
222
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000223 memset(rta_tb, 0, sizeof(rta_tb));
224 parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
225
226 if (!rta_tb[IFA_LOCAL])
227 rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
228 if (!rta_tb[IFA_ADDRESS])
229 rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
230
231 if (filter.ifindex && filter.ifindex != ifa->ifa_index)
232 return 0;
233 if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
234 return 0;
235 if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
236 return 0;
237 if (filter.label) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000238 const char *label;
239 if (rta_tb[IFA_LABEL])
240 label = RTA_DATA(rta_tb[IFA_LABEL]);
241 else
242 label = ll_idx_n2a(ifa->ifa_index, b1);
243 if (fnmatch(filter.label, label, 0) != 0)
244 return 0;
245 }
246 if (filter.pfx.family) {
247 if (rta_tb[IFA_LOCAL]) {
248 inet_prefix dst;
249 memset(&dst, 0, sizeof(dst));
250 dst.family = ifa->ifa_family;
251 memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
252 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
253 return 0;
254 }
255 }
256
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000257 if (filter.flushb) {
258 struct nlmsghdr *fn;
259 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
260 if (flush_update())
261 return -1;
262 }
263 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
264 memcpy(fn, n, n->nlmsg_len);
265 fn->nlmsg_type = RTM_DELADDR;
266 fn->nlmsg_flags = NLM_F_REQUEST;
267 fn->nlmsg_seq = ++filter.rth->seq;
268 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
269 filter.flushed++;
270 return 0;
271 }
272
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000273 if (n->nlmsg_type == RTM_DELADDR)
274 fprintf(fp, "Deleted ");
275
276 if (filter.oneline)
277 fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
278 if (ifa->ifa_family == AF_INET)
279 fprintf(fp, " inet ");
280 else if (ifa->ifa_family == AF_INET6)
281 fprintf(fp, " inet6 ");
282 else
283 fprintf(fp, " family %d ", ifa->ifa_family);
284
285 if (rta_tb[IFA_LOCAL]) {
286 fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
287 RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
288 RTA_DATA(rta_tb[IFA_LOCAL]),
289 abuf, sizeof(abuf)));
290
291 if (rta_tb[IFA_ADDRESS] == NULL ||
292 memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
293 fprintf(fp, "/%d ", ifa->ifa_prefixlen);
294 } else {
295 fprintf(fp, " peer %s/%d ",
296 rt_addr_n2a(ifa->ifa_family,
297 RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
298 RTA_DATA(rta_tb[IFA_ADDRESS]),
299 abuf, sizeof(abuf)),
300 ifa->ifa_prefixlen);
301 }
302 }
303
304 if (rta_tb[IFA_BROADCAST]) {
305 fprintf(fp, "brd %s ",
306 rt_addr_n2a(ifa->ifa_family,
307 RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
308 RTA_DATA(rta_tb[IFA_BROADCAST]),
309 abuf, sizeof(abuf)));
310 }
311 if (rta_tb[IFA_ANYCAST]) {
312 fprintf(fp, "any %s ",
313 rt_addr_n2a(ifa->ifa_family,
314 RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
315 RTA_DATA(rta_tb[IFA_ANYCAST]),
316 abuf, sizeof(abuf)));
317 }
318 fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
319 if (ifa->ifa_flags&IFA_F_SECONDARY) {
320 ifa->ifa_flags &= ~IFA_F_SECONDARY;
321 fprintf(fp, "secondary ");
322 }
323 if (ifa->ifa_flags&IFA_F_TENTATIVE) {
324 ifa->ifa_flags &= ~IFA_F_TENTATIVE;
325 fprintf(fp, "tentative ");
326 }
327 if (ifa->ifa_flags&IFA_F_DEPRECATED) {
328 ifa->ifa_flags &= ~IFA_F_DEPRECATED;
329 fprintf(fp, "deprecated ");
330 }
331 if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
332 fprintf(fp, "dynamic ");
333 } else
334 ifa->ifa_flags &= ~IFA_F_PERMANENT;
335 if (ifa->ifa_flags)
336 fprintf(fp, "flags %02x ", ifa->ifa_flags);
337 if (rta_tb[IFA_LABEL])
338 fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
339 if (rta_tb[IFA_CACHEINFO]) {
340 struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
341 char buf[128];
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000342 fputc(_SL_, fp);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000343 if (ci->ifa_valid == 0xFFFFFFFFU)
344 sprintf(buf, "valid_lft forever");
345 else
346 sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
347 if (ci->ifa_prefered == 0xFFFFFFFFU)
348 sprintf(buf+strlen(buf), " preferred_lft forever");
349 else
350 sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
351 fprintf(fp, " %s", buf);
352 }
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000353 fputc('\n', fp);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000354 fflush(fp);
355 return 0;
356}
357
358
359struct nlmsg_list
360{
361 struct nlmsg_list *next;
362 struct nlmsghdr h;
363};
364
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000365static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000366{
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000367 for (; ainfo; ainfo = ainfo->next) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000368 struct nlmsghdr *n = &ainfo->h;
369 struct ifaddrmsg *ifa = NLMSG_DATA(n);
370
371 if (n->nlmsg_type != RTM_NEWADDR)
372 continue;
373
374 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
375 return -1;
376
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000377 if (ifa->ifa_index != ifindex ||
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000378 (filter.family && filter.family != ifa->ifa_family))
379 continue;
380
381 print_addrinfo(NULL, n, fp);
382 }
383 return 0;
384}
385
386
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000387static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000388{
389 struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
390 struct nlmsg_list *h;
391 struct nlmsg_list **lp;
392
393 h = malloc(n->nlmsg_len+sizeof(void*));
394 if (h == NULL)
395 return -1;
396
397 memcpy(&h->h, n, n->nlmsg_len);
398 h->next = NULL;
399
400 for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
401 *lp = h;
402
403 ll_remember_index(who, n, NULL);
404 return 0;
405}
406
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000407static void ipaddr_reset_filter(int _oneline)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000408{
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000409 memset(&filter, 0, sizeof(filter));
410 filter.oneline = _oneline;
411}
412
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000413/* Return value becomes exitcode. It's okay to not return at all */
Rob Landleydfba7412006-03-06 20:47:33 +0000414int ipaddr_list_or_flush(int argc, char **argv, int flush)
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000415{
Rob Landley0a7c8ef2006-02-22 17:01:00 +0000416 static const char *const option[] = { "to", "scope", "up", "label", "dev", 0 };
417
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000418 struct nlmsg_list *linfo = NULL;
419 struct nlmsg_list *ainfo = NULL;
420 struct nlmsg_list *l;
421 struct rtnl_handle rth;
422 char *filter_dev = NULL;
423 int no_link = 0;
424
425 ipaddr_reset_filter(oneline);
426 filter.showqueue = 1;
427
428 if (filter.family == AF_UNSPEC)
429 filter.family = preferred_family;
430
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000431 if (flush) {
432 if (argc <= 0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000433 bb_error_msg_and_die(bb_msg_requires_arg, "flush");
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000434 }
435 if (filter.family == AF_PACKET) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000436 bb_error_msg_and_die("cannot flush link addresses");
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000437 }
438 }
439
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000440 while (argc > 0) {
Denis Vlasenko5af906e2006-11-05 18:05:09 +0000441 const int option_num = index_in_str_array(option, *argv);
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000442 switch (option_num) {
443 case 0: /* to */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000444 NEXT_ARG();
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000445 get_prefix(&filter.pfx, *argv, filter.family);
446 if (filter.family == AF_UNSPEC) {
447 filter.family = filter.pfx.family;
448 }
449 break;
450 case 1: /* scope */
451 {
Eric Andersend78aea82006-01-30 18:00:02 +0000452 uint32_t scope = 0;
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000453 NEXT_ARG();
454 filter.scopemask = -1;
455 if (rtnl_rtscope_a2n(&scope, *argv)) {
456 if (strcmp(*argv, "all") != 0) {
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000457 invarg(*argv, "scope");
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000458 }
459 scope = RT_SCOPE_NOWHERE;
460 filter.scopemask = 0;
461 }
462 filter.scope = scope;
463 break;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000464 }
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000465 case 2: /* up */
466 filter.up = 1;
467 break;
468 case 3: /* label */
469 NEXT_ARG();
470 filter.label = *argv;
471 break;
472 case 4: /* dev */
473 NEXT_ARG();
474 default:
475 if (filter_dev) {
476 duparg2("dev", *argv);
477 }
478 filter_dev = *argv;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000479 }
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000480 argv++;
481 argc--;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000482 }
483
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000484 xrtnl_open(&rth);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000485
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000486 xrtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK);
487 xrtnl_dump_filter(&rth, store_nlmsg, &linfo);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000488
489 if (filter_dev) {
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000490 filter.ifindex = xll_name_to_index(filter_dev);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000491 }
492
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000493 if (flush) {
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000494 char flushb[4096-512];
495
496 filter.flushb = flushb;
497 filter.flushp = 0;
498 filter.flushe = sizeof(flushb);
499 filter.rth = &rth;
500
501 for (;;) {
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000502 xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000503 filter.flushed = 0;
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000504 xrtnl_dump_filter(&rth, print_addrinfo, stdout);
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000505 if (filter.flushed == 0) {
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000506 return 0;
507 }
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000508 if (flush_update() < 0)
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000509 return 1;
Glenn L McGrathd66370c2003-01-13 21:40:38 +0000510 }
511 }
512
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000513 if (filter.family != AF_PACKET) {
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000514 xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
515 xrtnl_dump_filter(&rth, store_nlmsg, &ainfo);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000516 }
517
518
519 if (filter.family && filter.family != AF_PACKET) {
520 struct nlmsg_list **lp;
521 lp=&linfo;
522
523 if (filter.oneline)
524 no_link = 1;
525
526 while ((l=*lp)!=NULL) {
527 int ok = 0;
528 struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
529 struct nlmsg_list *a;
530
531 for (a=ainfo; a; a=a->next) {
532 struct nlmsghdr *n = &a->h;
533 struct ifaddrmsg *ifa = NLMSG_DATA(n);
534
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000535 if (ifa->ifa_index != ifi->ifi_index ||
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000536 (filter.family && filter.family != ifa->ifa_family))
537 continue;
538 if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
539 continue;
540 if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
541 continue;
542 if (filter.pfx.family || filter.label) {
543 struct rtattr *tb[IFA_MAX+1];
544 memset(tb, 0, sizeof(tb));
545 parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
546 if (!tb[IFA_LOCAL])
547 tb[IFA_LOCAL] = tb[IFA_ADDRESS];
548
549 if (filter.pfx.family && tb[IFA_LOCAL]) {
550 inet_prefix dst;
551 memset(&dst, 0, sizeof(dst));
552 dst.family = ifa->ifa_family;
553 memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
554 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
555 continue;
556 }
557 if (filter.label) {
558 SPRINT_BUF(b1);
559 const char *label;
560 if (tb[IFA_LABEL])
561 label = RTA_DATA(tb[IFA_LABEL]);
562 else
563 label = ll_idx_n2a(ifa->ifa_index, b1);
564 if (fnmatch(filter.label, label, 0) != 0)
565 continue;
566 }
567 }
568
569 ok = 1;
570 break;
571 }
572 if (!ok)
573 *lp = l->next;
574 else
575 lp = &l->next;
576 }
577 }
578
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000579 for (l = linfo; l; l = l->next) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000580 if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
581 struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
582 if (filter.family != AF_PACKET)
583 print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
584 }
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000585 fflush(stdout); /* why? */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000586 }
587
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000588 return 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000589}
590
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000591static int default_scope(inet_prefix *lcl)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000592{
593 if (lcl->family == AF_INET) {
Denis Vlasenko98ee06d2006-12-31 18:57:37 +0000594 if (lcl->bytelen >= 1 && *(uint8_t*)&lcl->data == 127)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000595 return RT_SCOPE_HOST;
596 }
597 return 0;
598}
599
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000600/* Return value becomes exitcode. It's okay to not return at all */
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000601static int ipaddr_modify(int cmd, int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000602{
Rob Landley0a7c8ef2006-02-22 17:01:00 +0000603 static const char *const option[] = {
604 "peer", "remote", "broadcast", "brd",
605 "anycast", "scope", "dev", "label", "local", 0
606 };
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000607 struct rtnl_handle rth;
608 struct {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000609 struct nlmsghdr n;
610 struct ifaddrmsg ifa;
611 char buf[256];
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000612 } req;
613 char *d = NULL;
614 char *l = NULL;
615 inet_prefix lcl;
616 inet_prefix peer;
617 int local_len = 0;
618 int peer_len = 0;
619 int brd_len = 0;
620 int any_len = 0;
Bernhard Reutner-Fischercd0e80c2007-06-20 14:53:49 +0000621 bool scoped = 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000622
623 memset(&req, 0, sizeof(req));
624
625 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
626 req.n.nlmsg_flags = NLM_F_REQUEST;
627 req.n.nlmsg_type = cmd;
628 req.ifa.ifa_family = preferred_family;
629
630 while (argc > 0) {
Denis Vlasenko5af906e2006-11-05 18:05:09 +0000631 const int option_num = index_in_str_array(option, *argv);
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000632 switch (option_num) {
633 case 0: /* peer */
634 case 1: /* remote */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000635 NEXT_ARG();
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000636
637 if (peer_len) {
638 duparg("peer", *argv);
639 }
640 get_prefix(&peer, *argv, req.ifa.ifa_family);
641 peer_len = peer.bytelen;
642 if (req.ifa.ifa_family == AF_UNSPEC) {
643 req.ifa.ifa_family = peer.family;
644 }
645 addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
646 req.ifa.ifa_prefixlen = peer.bitlen;
647 break;
648 case 2: /* broadcast */
649 case 3: /* brd */
650 {
651 inet_prefix addr;
652 NEXT_ARG();
653 if (brd_len) {
654 duparg("broadcast", *argv);
655 }
Denis Vlasenkobf66fbc2006-12-21 13:23:14 +0000656 if (LONE_CHAR(*argv, '+')) {
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000657 brd_len = -1;
658 }
Denis Vlasenko9f739442006-12-16 23:49:13 +0000659 else if (LONE_DASH(*argv)) {
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000660 brd_len = -2;
661 } else {
662 get_addr(&addr, *argv, req.ifa.ifa_family);
663 if (req.ifa.ifa_family == AF_UNSPEC)
664 req.ifa.ifa_family = addr.family;
665 addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
666 brd_len = addr.bytelen;
667 }
668 break;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000669 }
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000670 case 4: /* anycast */
671 {
672 inet_prefix addr;
673 NEXT_ARG();
674 if (any_len) {
675 duparg("anycast", *argv);
676 }
677 get_addr(&addr, *argv, req.ifa.ifa_family);
678 if (req.ifa.ifa_family == AF_UNSPEC) {
679 req.ifa.ifa_family = addr.family;
680 }
681 addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
682 any_len = addr.bytelen;
683 break;
684 }
685 case 5: /* scope */
686 {
Eric Andersend78aea82006-01-30 18:00:02 +0000687 uint32_t scope = 0;
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000688 NEXT_ARG();
689 if (rtnl_rtscope_a2n(&scope, *argv)) {
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000690 invarg(*argv, "scope");
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000691 }
692 req.ifa.ifa_scope = scope;
693 scoped = 1;
694 break;
695 }
696 case 6: /* dev */
697 NEXT_ARG();
698 d = *argv;
699 break;
700 case 7: /* label */
701 NEXT_ARG();
702 l = *argv;
703 addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
704 break;
705 case 8: /* local */
706 NEXT_ARG();
707 default:
708 if (local_len) {
709 duparg2("local", *argv);
710 }
711 get_prefix(&lcl, *argv, req.ifa.ifa_family);
712 if (req.ifa.ifa_family == AF_UNSPEC) {
713 req.ifa.ifa_family = lcl.family;
714 }
715 addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
716 local_len = lcl.bytelen;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000717 }
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000718 argc--;
719 argv++;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000720 }
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000721
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000722 if (d == NULL) {
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000723 bb_error_msg(bb_msg_requires_arg,"\"dev\"");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000724 return -1;
725 }
Bernhard Reutner-Fischercd0e80c2007-06-20 14:53:49 +0000726 if (l && strncmp(d, l, strlen(d)) != 0) {
Rob Landley0a7c8ef2006-02-22 17:01:00 +0000727 bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000728 }
729
730 if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
731 peer = lcl;
732 addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
733 }
734 if (req.ifa.ifa_prefixlen == 0)
735 req.ifa.ifa_prefixlen = lcl.bitlen;
736
737 if (brd_len < 0 && cmd != RTM_DELADDR) {
738 inet_prefix brd;
739 int i;
740 if (req.ifa.ifa_family != AF_INET) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000741 bb_error_msg_and_die("broadcast can be set only for IPv4 addresses");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000742 }
743 brd = peer;
744 if (brd.bitlen <= 30) {
745 for (i=31; i>=brd.bitlen; i--) {
746 if (brd_len == -1)
747 brd.data[0] |= htonl(1<<(31-i));
748 else
749 brd.data[0] &= ~htonl(1<<(31-i));
750 }
751 addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
752 brd_len = brd.bytelen;
753 }
754 }
755 if (!scoped && cmd != RTM_DELADDR)
756 req.ifa.ifa_scope = default_scope(&lcl);
757
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000758 xrtnl_open(&rth);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000759
760 ll_init_map(&rth);
761
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000762 req.ifa.ifa_index = xll_name_to_index(d);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000763
764 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000765 return 2;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000766
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000767 return 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000768}
769
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000770/* Return value becomes exitcode. It's okay to not return at all */
Rob Landleydfba7412006-03-06 20:47:33 +0000771int do_ipaddr(int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000772{
Rob Landley0a7c8ef2006-02-22 17:01:00 +0000773 static const char *const commands[] = {
Denis Vlasenko5af906e2006-11-05 18:05:09 +0000774 "add", "delete", "list", "show", "lst", "flush", 0
Rob Landley0a7c8ef2006-02-22 17:01:00 +0000775 };
776
Bernhard Reutner-Fischercd0e80c2007-06-20 14:53:49 +0000777 int command_num = 2; /* default command is list */
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000778
779 if (*argv) {
Denis Vlasenko5af906e2006-11-05 18:05:09 +0000780 command_num = index_in_substr_array(commands, *argv);
Glenn L McGrath2626ef62002-12-02 01:40:05 +0000781 }
Bernhard Reutner-Fischercd0e80c2007-06-20 14:53:49 +0000782 if (command_num < 0 || command_num > 5)
783 bb_error_msg_and_die("unknown command %s", *argv);
784 --argc;
785 ++argv;
786 if (command_num == 0) /* add */
787 return ipaddr_modify(RTM_NEWADDR, argc, argv);
788 else if (command_num == 1) /* delete */
789 return ipaddr_modify(RTM_DELADDR, argc, argv);
790 else if (command_num == 5) /* flush */
791 return ipaddr_list_or_flush(argc, argv, 1);
792 else /* 2 == list, 3 == show, 4 == lst */
793 return ipaddr_list_or_flush(argc, argv, 0);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000794}