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