Make more glibc friendly.
[platform/upstream/net-tools.git] / ipmaddr.c
1 /*
2  * ipmaddr.c            "ip maddress".
3  *
4  *              This program is free software; you can redistribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <syslog.h>
17 #include <fcntl.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <string.h>
23
24 #if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
25 #include <net/if.h>
26 #else
27 #include <linux/if.h>
28 #endif
29
30 #include "config.h"
31 #include "intl.h"
32 #include "util-ank.h"
33 #include "net-support.h"
34 #include "version.h"
35 #include "pathnames.h"
36
37 char filter_dev[16];
38 int  filter_family;
39
40 /* These have nothing to do with rtnetlink. :-) */
41 #define NEWADDR         1
42 #define DELADDR         2
43
44 char *Release = RELEASE,
45      *Version = "ipmaddr 1.0",
46      *Signature = "Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>";
47
48 static void version(void)
49 {
50         printf("%s\n%s\n%s\n", Release, Version, Signature);
51         exit(E_VERSION);
52 }
53
54 static void usage(void) __attribute__((noreturn));
55
56 static void usage(void)
57 {
58         fprintf(stderr, _("Usage: ipmaddr [ add | del ] MULTIADDR dev STRING\n"));
59         fprintf(stderr, _("       ipmaddr show [ dev STRING ] [ ipv4 | ipv6 | link | all ]\n"));
60         fprintf(stderr, _("       ipmaddr -V | -version\n"));
61         exit(-1);
62 }
63
64 static void print_lla(FILE *fp, int len, unsigned char *addr)
65 {
66         int i;
67         for (i=0; i<len; i++) {
68                 if (i==0)
69                         fprintf(fp, "%02x", addr[i]);
70                 else
71                         fprintf(fp, ":%02x", addr[i]);
72         }
73 }
74
75 static int parse_lla(char *str, unsigned char *addr)
76 {
77         int len=0;
78
79         while (*str) {
80                 int tmp;
81                 if (str[0] == ':' || str[0] == '.') {
82                         str++;
83                         continue;
84                 }
85                 if (str[1] == 0)
86                         return -1;
87                 if (sscanf(str, "%02x", &tmp) != 1)
88                         return -1;
89                 addr[len] = tmp;
90                 len++;
91                 str += 2;
92         }
93         return len;
94 }
95
96 static int parse_hex(char *str, unsigned char *addr)
97 {
98         int len=0;
99
100         while (*str) {
101                 int tmp;
102                 if (str[1] == 0)
103                         return -1;
104                 if (sscanf(str, "%02x", &tmp) != 1)
105                         return -1;
106                 addr[len] = tmp;
107                 len++;
108                 str += 2;
109         }
110         return len;
111 }
112
113 struct ma_info
114 {
115         struct ma_info *next;
116         int             index;
117         int             users;
118         char            *features;
119         char            name[IFNAMSIZ];
120         inet_prefix     addr;
121 };
122
123 void maddr_ins(struct ma_info **lst, struct ma_info *m)
124 {
125         struct ma_info *mp;
126
127         for (; (mp=*lst) != NULL; lst = &mp->next) {
128                 if (mp->index > m->index)
129                         break;
130         }
131         m->next = *lst;
132         *lst = m;
133 }
134
135 void read_dev_mcast(struct ma_info **result_p)
136 {
137         char buf[256];
138         FILE *fp = fopen(_PATH_PROCNET_DEV_MCAST, "r");
139
140         if (!fp)
141                 return;
142
143         while (fgets(buf, sizeof(buf), fp)) {
144                 char hexa[256];
145                 struct ma_info m;
146                 int len;
147                 int st;
148
149                 memset(&m, 0, sizeof(m));
150                 sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st,
151                        hexa);
152                 if (filter_dev[0] && strcmp(filter_dev, m.name))
153                         continue;
154
155                 m.addr.family = AF_PACKET;
156
157                 len = parse_hex(hexa, (unsigned char*)&m.addr.data);
158                 if (len >= 0) {
159                         struct ma_info *ma = malloc(sizeof(m));
160
161                         memcpy(ma, &m, sizeof(m));
162                         ma->addr.bytelen = len;
163                         ma->addr.bitlen = len<<3;
164                         if (st)
165                                 ma->features = "static";
166                         maddr_ins(result_p, ma);
167                 }
168         }
169         fclose(fp);
170 }
171
172 void read_igmp(struct ma_info **result_p)
173 {
174         struct ma_info m;
175         char buf[256];
176         FILE *fp = fopen(_PATH_PROCNET_IGMP, "r");
177
178         if (!fp)
179                 return;
180         memset(&m, 0, sizeof(m));
181         fgets(buf, sizeof(buf), fp);
182
183         m.addr.family = AF_INET;
184         m.addr.bitlen = 32;
185         m.addr.bytelen = 4;
186
187         while (fgets(buf, sizeof(buf), fp)) {
188                 struct ma_info *ma = malloc(sizeof(m));
189
190                 if (buf[0] != '\t') {
191                         sscanf(buf, "%d%s", &m.index, m.name);
192                         continue;
193                 }
194
195                 if (filter_dev[0] && strcmp(filter_dev, m.name))
196                         continue;
197
198                 sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users);
199
200                 ma = malloc(sizeof(m));
201                 memcpy(ma, &m, sizeof(m));
202                 maddr_ins(result_p, ma);
203         }
204         fclose(fp);
205 }
206
207
208 void read_igmp6(struct ma_info **result_p)
209 {
210         char buf[256];
211         FILE *fp = fopen(_PATH_PROCNET_IGMP6, "r");
212
213         if (!fp)
214                 return;
215
216         while (fgets(buf, sizeof(buf), fp)) {
217                 char hexa[256];
218                 struct ma_info m;
219                 int len;
220
221                 memset(&m, 0, sizeof(m));
222                 sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users);
223
224                 if (filter_dev[0] && strcmp(filter_dev, m.name))
225                         continue;
226
227                 m.addr.family = AF_INET6;
228
229                 len = parse_hex(hexa, (unsigned char*)&m.addr.data);
230                 if (len >= 0) {
231                         struct ma_info *ma = malloc(sizeof(m));
232
233                         memcpy(ma, &m, sizeof(m));
234
235                         ma->addr.bytelen = len;
236                         ma->addr.bitlen = len<<3;
237                         maddr_ins(result_p, ma);
238                 }
239         }
240         fclose(fp);
241 }
242
243 static void print_maddr(FILE *fp, struct ma_info *list)
244 {
245         fprintf(fp, "\t");
246
247         if (list->addr.family == AF_PACKET) {
248                 fprintf(fp, "link  ");
249                 print_lla(fp, list->addr.bytelen, (unsigned char*)list->addr.data);
250         } else {
251                 char abuf[256];
252                 switch(list->addr.family) {
253                 case AF_INET:
254                         fprintf(fp, "inet  ");
255                         break;
256                 case AF_INET6:
257                         fprintf(fp, "inet6 ");
258                         break;
259                 default:
260                         fprintf(fp, _("family %d "), list->addr.family);
261                         break;
262                 }
263                 if (format_host(list->addr.family, list->addr.data, abuf, sizeof(abuf)))
264                         fprintf(fp, "%s", abuf);
265                 else
266                         fprintf(fp, "?");
267         }
268         if (list->users != 1)
269                 fprintf(fp, _(" users %d"), list->users);
270         if (list->features)
271                 fprintf(fp, " %s", list->features);
272         fprintf(fp, "\n");
273 }
274
275 static void print_mlist(FILE *fp, struct ma_info *list)
276 {
277         int cur_index = 0;
278
279         for (; list; list = list->next) {
280                 if (cur_index != list->index) {
281                         cur_index = list->index;
282                         fprintf(fp, "%d:\t%s\n", cur_index, list->name);
283                 }
284                 print_maddr(fp, list);
285         }
286 }
287
288 static int multiaddr_list(int argc, char **argv)
289 {
290         struct ma_info *list = NULL;
291
292         while (argc > 0) {
293                 if (strcmp(*argv, "dev") == 0) {
294                         NEXT_ARG();
295                         if (filter_dev[0])
296                                 usage();
297                         strcpy(filter_dev, *argv);
298                 } else if (strcmp(*argv, "all") == 0) {
299                         filter_family = AF_UNSPEC;
300                 } else if (strcmp(*argv, "ipv4") == 0) {
301                         filter_family = AF_INET;
302                 } else if (strcmp(*argv, "ipv6") == 0) {
303                         filter_family = AF_INET6;
304                 } else if (strcmp(*argv, "link") == 0) {
305                         filter_family = AF_PACKET;
306                 } else {
307                         if (filter_dev[0])
308                                 usage();
309                         strcpy(filter_dev, *argv);
310                 }
311                 argv++; argc--;
312         }
313
314         if (!filter_family || filter_family == AF_PACKET)
315                 read_dev_mcast(&list);
316         if (!filter_family || filter_family == AF_INET)
317                 read_igmp(&list);
318         if (!filter_family || filter_family == AF_INET6)
319                 read_igmp6(&list);
320         print_mlist(stdout, list);
321         return 0;
322 }
323
324 int multiaddr_modify(int cmd, int argc, char **argv)
325 {
326         struct ifreq ifr;
327         int fd;
328
329         memset(&ifr, 0, sizeof(ifr));
330
331         if (cmd == NEWADDR)
332                 cmd = SIOCADDMULTI;
333         else
334                 cmd = SIOCDELMULTI;
335
336         while (argc > 0) {
337                 if (strcmp(*argv, "dev") == 0) {
338                         NEXT_ARG();
339                         if (ifr.ifr_name[0])
340                                 usage();
341                         strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
342                 } else {
343                         if (ifr.ifr_hwaddr.sa_data[0])
344                                 usage();
345                         if (parse_lla(*argv, ifr.ifr_hwaddr.sa_data) < 0)
346                                 usage();
347                 }
348                 argc--; argv++;
349         }
350         if (ifr.ifr_name[0] == 0)
351                 usage();
352
353         fd = socket(AF_INET, SOCK_DGRAM, 0);
354         if (fd < 0) {
355                 perror(_("Cannot create socket"));
356                 exit(1);
357         }
358         if (ioctl(fd, cmd, (char*)&ifr) != 0) {
359                 perror("ioctl");
360                 exit(1);
361         }
362         close(fd);
363
364         exit(0);
365 }
366
367
368 int do_multiaddr(int argc, char **argv)
369 {
370         if (argc < 1)
371                 return multiaddr_list(0, NULL);
372         if (matches(*argv, "add") == 0)
373                 return multiaddr_modify(NEWADDR, argc-1, argv+1);
374         if (matches(*argv, "delete") == 0)
375                 return multiaddr_modify(DELADDR, argc-1, argv+1);
376         if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
377             || matches(*argv, "lst") == 0)
378                 return multiaddr_list(argc-1, argv+1);
379         usage();
380 }
381
382 int preferred_family = AF_UNSPEC;
383 int show_stats = 0;
384 int resolve_hosts = 0;
385
386 int main(int argc, char **argv)
387 {
388         char *basename;
389
390 #if I18N
391         bindtextdomain("net-tools", "/usr/share/locale");
392         textdomain("net-tools");
393 #endif
394
395         basename = strrchr(argv[0], '/');
396         if (basename == NULL)
397                 basename = argv[0];
398         else
399                 basename++;
400         
401         while (argc > 1) {
402                 if (argv[1][0] != '-')
403                         break;
404                 if (matches(argv[1], "-family") == 0) {
405                         argc--;
406                         argv++;
407                         if (argc <= 1)
408                                 usage();
409                         if (strcmp(argv[1], "inet") == 0)
410                                 preferred_family = AF_INET;
411                         else if (strcmp(argv[1], "inet6") == 0)
412                                 preferred_family = AF_INET6;
413                         else
414                                 usage();
415                 } else if (matches(argv[1], "-stats") == 0 ||
416                            matches(argv[1], "-statistics") == 0) {
417                         ++show_stats;
418                 } else if (matches(argv[1], "-resolve") == 0) {
419                         ++resolve_hosts;
420                 } else if ((matches(argv[1], "-V") == 0) || matches(argv[1], "--version") == 0) {
421                         version();
422                 } else
423                         usage();
424                 argc--; argv++;
425         }
426
427         return do_multiaddr(argc-1, argv+1);
428 }