Home | History | Annotate | Download | only in ip
      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 (at) 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 #include <linux/netdevice.h>
     25 #include <linux/if.h>
     26 #include <linux/if_arp.h>
     27 #include <linux/sockios.h>
     28 
     29 #include "rt_names.h"
     30 #include "utils.h"
     31 #include "ip_common.h"
     32 
     33 static struct {
     34 	char *dev;
     35 	int  family;
     36 } filter;
     37 
     38 static void usage(void) __attribute__((noreturn));
     39 
     40 static void usage(void)
     41 {
     42 	fprintf(stderr, "Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n");
     43 	fprintf(stderr, "       ip maddr show [ dev STRING ]\n");
     44 	exit(-1);
     45 }
     46 
     47 static int parse_hex(char *str, unsigned char *addr, size_t size)
     48 {
     49 	int len = 0;
     50 
     51 	while (*str && (len < 2 * size)) {
     52 		int tmp;
     53 		if (str[1] == 0)
     54 			return -1;
     55 		if (sscanf(str, "%02x", &tmp) != 1)
     56 			return -1;
     57 		addr[len] = tmp;
     58 		len++;
     59 		str += 2;
     60 	}
     61 	return len;
     62 }
     63 
     64 struct ma_info
     65 {
     66 	struct ma_info *next;
     67 	int		index;
     68 	int		users;
     69 	char		*features;
     70 	char		name[IFNAMSIZ];
     71 	inet_prefix	addr;
     72 };
     73 
     74 static void maddr_ins(struct ma_info **lst, struct ma_info *m)
     75 {
     76 	struct ma_info *mp;
     77 
     78 	for (; (mp = *lst) != NULL; lst = &mp->next) {
     79 		if (mp->index > m->index)
     80 			break;
     81 	}
     82 	m->next = *lst;
     83 	*lst = m;
     84 }
     85 
     86 static void read_dev_mcast(struct ma_info **result_p)
     87 {
     88 	char buf[256];
     89 	FILE *fp = fopen("/proc/net/dev_mcast", "r");
     90 
     91 	if (!fp)
     92 		return;
     93 
     94 	while (fgets(buf, sizeof(buf), fp)) {
     95 		char hexa[256];
     96 		struct ma_info m;
     97 		int len;
     98 		int st;
     99 
    100 		memset(&m, 0, sizeof(m));
    101 		sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st,
    102 		       hexa);
    103 		if (filter.dev && strcmp(filter.dev, m.name))
    104 			continue;
    105 
    106 		m.addr.family = AF_PACKET;
    107 
    108 		len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data));
    109 		if (len >= 0) {
    110 			struct ma_info *ma = malloc(sizeof(m));
    111 
    112 			memcpy(ma, &m, sizeof(m));
    113 			ma->addr.bytelen = len;
    114 			ma->addr.bitlen = len<<3;
    115 			if (st)
    116 				ma->features = "static";
    117 			maddr_ins(result_p, ma);
    118 		}
    119 	}
    120 	fclose(fp);
    121 }
    122 
    123 static void read_igmp(struct ma_info **result_p)
    124 {
    125 	struct ma_info m;
    126 	char buf[256];
    127 	FILE *fp = fopen("/proc/net/igmp", "r");
    128 
    129 	if (!fp)
    130 		return;
    131 	memset(&m, 0, sizeof(m));
    132 	if (!fgets(buf, sizeof(buf), fp)) {
    133 		fclose(fp);
    134 		return;
    135 	}
    136 
    137 	m.addr.family = AF_INET;
    138 	m.addr.bitlen = 32;
    139 	m.addr.bytelen = 4;
    140 
    141 	while (fgets(buf, sizeof(buf), fp)) {
    142 		struct ma_info *ma;
    143 
    144 		if (buf[0] != '\t') {
    145 			sscanf(buf, "%d%s", &m.index, m.name);
    146 			continue;
    147 		}
    148 
    149 		if (filter.dev && strcmp(filter.dev, m.name))
    150 			continue;
    151 
    152 		sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users);
    153 
    154 		ma = malloc(sizeof(m));
    155 		memcpy(ma, &m, sizeof(m));
    156 		maddr_ins(result_p, ma);
    157 	}
    158 	fclose(fp);
    159 }
    160 
    161 
    162 static void read_igmp6(struct ma_info **result_p)
    163 {
    164 	char buf[256];
    165 	FILE *fp = fopen("/proc/net/igmp6", "r");
    166 
    167 	if (!fp)
    168 		return;
    169 
    170 	while (fgets(buf, sizeof(buf), fp)) {
    171 		char hexa[256];
    172 		struct ma_info m;
    173 		int len;
    174 
    175 		memset(&m, 0, sizeof(m));
    176 		sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users);
    177 
    178 		if (filter.dev && strcmp(filter.dev, m.name))
    179 			continue;
    180 
    181 		m.addr.family = AF_INET6;
    182 
    183 		len = parse_hex(hexa, (unsigned char*)&m.addr.data, sizeof (m.addr.data));
    184 		if (len >= 0) {
    185 			struct ma_info *ma = malloc(sizeof(m));
    186 
    187 			memcpy(ma, &m, sizeof(m));
    188 
    189 			ma->addr.bytelen = len;
    190 			ma->addr.bitlen = len<<3;
    191 			maddr_ins(result_p, ma);
    192 		}
    193 	}
    194 	fclose(fp);
    195 }
    196 
    197 static void print_maddr(FILE *fp, struct ma_info *list)
    198 {
    199 	fprintf(fp, "\t");
    200 
    201 	if (list->addr.family == AF_PACKET) {
    202 		SPRINT_BUF(b1);
    203 		fprintf(fp, "link  %s", ll_addr_n2a((unsigned char*)list->addr.data,
    204 						    list->addr.bytelen, 0,
    205 						    b1, sizeof(b1)));
    206 	} else {
    207 		char abuf[256];
    208 		switch(list->addr.family) {
    209 		case AF_INET:
    210 			fprintf(fp, "inet  ");
    211 			break;
    212 		case AF_INET6:
    213 			fprintf(fp, "inet6 ");
    214 			break;
    215 		default:
    216 			fprintf(fp, "family %d ", list->addr.family);
    217 			break;
    218 		}
    219 		fprintf(fp, "%s",
    220 			format_host(list->addr.family,
    221 				    -1,
    222 				    list->addr.data,
    223 				    abuf, sizeof(abuf)));
    224 	}
    225 	if (list->users != 1)
    226 		fprintf(fp, " users %d", list->users);
    227 	if (list->features)
    228 		fprintf(fp, " %s", list->features);
    229 	fprintf(fp, "\n");
    230 }
    231 
    232 static void print_mlist(FILE *fp, struct ma_info *list)
    233 {
    234 	int cur_index = 0;
    235 
    236 	for (; list; list = list->next) {
    237 		if (oneline) {
    238 			cur_index = list->index;
    239 			fprintf(fp, "%d:\t%s%s", cur_index, list->name, _SL_);
    240 		} else if (cur_index != list->index) {
    241 			cur_index = list->index;
    242 			fprintf(fp, "%d:\t%s\n", cur_index, list->name);
    243 		}
    244 		print_maddr(fp, list);
    245 	}
    246 }
    247 
    248 static int multiaddr_list(int argc, char **argv)
    249 {
    250 	struct ma_info *list = NULL;
    251 
    252 	if (!filter.family)
    253 		filter.family = preferred_family;
    254 
    255 	while (argc > 0) {
    256 		if (1) {
    257 			if (strcmp(*argv, "dev") == 0) {
    258 				NEXT_ARG();
    259 			}
    260 			else if (matches(*argv, "help") == 0)
    261 				usage();
    262 			if (filter.dev)
    263 				duparg2("dev", *argv);
    264 			filter.dev = *argv;
    265 		}
    266 		argv++; argc--;
    267 	}
    268 
    269 	if (!filter.family || filter.family == AF_PACKET)
    270 		read_dev_mcast(&list);
    271 	if (!filter.family || filter.family == AF_INET)
    272 		read_igmp(&list);
    273 	if (!filter.family || filter.family == AF_INET6)
    274 		read_igmp6(&list);
    275 	print_mlist(stdout, list);
    276 	return 0;
    277 }
    278 
    279 static int multiaddr_modify(int cmd, int argc, char **argv)
    280 {
    281 	struct ifreq ifr;
    282 	int fd;
    283 
    284 	memset(&ifr, 0, sizeof(ifr));
    285 
    286 	if (cmd == RTM_NEWADDR)
    287 		cmd = SIOCADDMULTI;
    288 	else
    289 		cmd = SIOCDELMULTI;
    290 
    291 	while (argc > 0) {
    292 		if (strcmp(*argv, "dev") == 0) {
    293 			NEXT_ARG();
    294 			if (ifr.ifr_name[0])
    295 				duparg("dev", *argv);
    296 			strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
    297 		} else {
    298 			if (matches(*argv, "address") == 0) {
    299 				NEXT_ARG();
    300 			}
    301 			if (matches(*argv, "help") == 0)
    302 				usage();
    303 			if (ifr.ifr_hwaddr.sa_data[0])
    304 				duparg("address", *argv);
    305 			if (ll_addr_a2n(ifr.ifr_hwaddr.sa_data,
    306 					14, *argv) < 0) {
    307 				fprintf(stderr, "Error: \"%s\" is not a legal ll address.\n", *argv);
    308 				exit(1);
    309 			}
    310 		}
    311 		argc--; argv++;
    312 	}
    313 	if (ifr.ifr_name[0] == 0) {
    314 		fprintf(stderr, "Not enough information: \"dev\" is required.\n");
    315 		exit(-1);
    316 	}
    317 
    318 	fd = socket(AF_INET, SOCK_DGRAM, 0);
    319 	if (fd < 0) {
    320 		perror("Cannot create socket");
    321 		exit(1);
    322 	}
    323 	if (ioctl(fd, cmd, (char*)&ifr) != 0) {
    324 		perror("ioctl");
    325 		exit(1);
    326 	}
    327 	close(fd);
    328 
    329 	exit(0);
    330 }
    331 
    332 
    333 int do_multiaddr(int argc, char **argv)
    334 {
    335 	if (argc < 1)
    336 		return multiaddr_list(0, NULL);
    337 	if (matches(*argv, "add") == 0)
    338 		return multiaddr_modify(RTM_NEWADDR, argc-1, argv+1);
    339 	if (matches(*argv, "delete") == 0)
    340 		return multiaddr_modify(RTM_DELADDR, argc-1, argv+1);
    341 	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
    342 	    || matches(*argv, "lst") == 0)
    343 		return multiaddr_list(argc-1, argv+1);
    344 	if (matches(*argv, "help") == 0)
    345 		usage();
    346 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip maddr help\".\n", *argv);
    347 	exit(-1);
    348 }
    349