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