1 /*
2  * ll_map.c
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/socket.h>
19 #include <netinet/in.h>
20 #include <string.h>
21 #include <linux/if.h>
22 
23 #include "libnetlink.h"
24 #include "ll_map.h"
25 
26 extern unsigned int if_nametoindex (const char *);
27 
28 struct ll_cache
29 {
30 	struct ll_cache   *idx_next;
31 	unsigned	flags;
32 	int		index;
33 	unsigned short	type;
34 	unsigned short	alen;
35 	char		name[IFNAMSIZ];
36 	unsigned char	addr[20];
37 };
38 
39 #define IDXMAP_SIZE	1024
40 static struct ll_cache *idx_head[IDXMAP_SIZE];
41 
idxhead(int idx)42 static inline struct ll_cache *idxhead(int idx)
43 {
44 	return idx_head[idx & (IDXMAP_SIZE - 1)];
45 }
46 
ll_remember_index(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)47 int ll_remember_index(const struct sockaddr_nl *who,
48 		      struct nlmsghdr *n, void *arg)
49 {
50 	int h;
51 	struct ifinfomsg *ifi = NLMSG_DATA(n);
52 	struct ll_cache *im, **imp;
53 	struct rtattr *tb[IFLA_MAX+1];
54 
55 	if (n->nlmsg_type != RTM_NEWLINK)
56 		return 0;
57 
58 	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
59 		return -1;
60 
61 	memset(tb, 0, sizeof(tb));
62 	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
63 	if (tb[IFLA_IFNAME] == NULL)
64 		return 0;
65 
66 	h = ifi->ifi_index & (IDXMAP_SIZE - 1);
67 	for (imp = &idx_head[h]; (im=*imp)!=NULL; imp = &im->idx_next)
68 		if (im->index == ifi->ifi_index)
69 			break;
70 
71 	if (im == NULL) {
72 		im = malloc(sizeof(*im));
73 		if (im == NULL)
74 			return 0;
75 		im->idx_next = *imp;
76 		im->index = ifi->ifi_index;
77 		*imp = im;
78 	}
79 
80 	im->type = ifi->ifi_type;
81 	im->flags = ifi->ifi_flags;
82 	if (tb[IFLA_ADDRESS]) {
83 		int alen;
84 		im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
85 		if (alen > sizeof(im->addr))
86 			alen = sizeof(im->addr);
87 		memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
88 	} else {
89 		im->alen = 0;
90 		memset(im->addr, 0, sizeof(im->addr));
91 	}
92 	strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
93 	return 0;
94 }
95 
ll_idx_n2a(unsigned idx,char * buf)96 const char *ll_idx_n2a(unsigned idx, char *buf)
97 {
98 	const struct ll_cache *im;
99 
100 	if (idx == 0)
101 		return "*";
102 
103 	for (im = idxhead(idx); im; im = im->idx_next)
104 		if (im->index == idx)
105 			return im->name;
106 
107 	snprintf(buf, IFNAMSIZ, "if%d", idx);
108 	return buf;
109 }
110 
111 
ll_index_to_name(unsigned idx)112 const char *ll_index_to_name(unsigned idx)
113 {
114 	static char nbuf[IFNAMSIZ];
115 
116 	return ll_idx_n2a(idx, nbuf);
117 }
118 
ll_index_to_type(unsigned idx)119 int ll_index_to_type(unsigned idx)
120 {
121 	const struct ll_cache *im;
122 
123 	if (idx == 0)
124 		return -1;
125 	for (im = idxhead(idx); im; im = im->idx_next)
126 		if (im->index == idx)
127 			return im->type;
128 	return -1;
129 }
130 
ll_index_to_flags(unsigned idx)131 unsigned ll_index_to_flags(unsigned idx)
132 {
133 	const struct ll_cache *im;
134 
135 	if (idx == 0)
136 		return 0;
137 
138 	for (im = idxhead(idx); im; im = im->idx_next)
139 		if (im->index == idx)
140 			return im->flags;
141 	return 0;
142 }
143 
ll_index_to_addr(unsigned idx,unsigned char * addr,unsigned alen)144 unsigned ll_index_to_addr(unsigned idx, unsigned char *addr,
145 			  unsigned alen)
146 {
147 	const struct ll_cache *im;
148 
149 	if (idx == 0)
150 		return 0;
151 
152 	for (im = idxhead(idx); im; im = im->idx_next) {
153 		if (im->index == idx) {
154 			if (alen > sizeof(im->addr))
155 				alen = sizeof(im->addr);
156 			if (alen > im->alen)
157 				alen = im->alen;
158 			memcpy(addr, im->addr, alen);
159 			return alen;
160 		}
161 	}
162 	return 0;
163 }
164 
ll_name_to_index(const char * name)165 unsigned ll_name_to_index(const char *name)
166 {
167 	static char ncache[IFNAMSIZ];
168 	static int icache;
169 	struct ll_cache *im;
170 	int i;
171 	unsigned idx;
172 
173 	if (name == NULL)
174 		return 0;
175 
176 	if (icache && strcmp(name, ncache) == 0)
177 		return icache;
178 
179 	for (i=0; i<IDXMAP_SIZE; i++) {
180 		for (im = idx_head[i]; im; im = im->idx_next) {
181 			if (strcmp(im->name, name) == 0) {
182 				icache = im->index;
183 				strcpy(ncache, name);
184 				return im->index;
185 			}
186 		}
187 	}
188 
189 	idx = if_nametoindex(name);
190 	if (idx == 0)
191 		sscanf(name, "if%u", &idx);
192 	return idx;
193 }
194 
ll_init_map(struct rtnl_handle * rth)195 int ll_init_map(struct rtnl_handle *rth)
196 {
197 	static int initialized;
198 
199 	if (initialized)
200 		return 0;
201 
202 	if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
203 		perror("Cannot send dump request");
204 		exit(1);
205 	}
206 
207 	if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) {
208 		fprintf(stderr, "Dump terminated\n");
209 		exit(1);
210 	}
211 
212 	initialized = 1;
213 
214 	return 0;
215 }
216