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