]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
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> | |
aba5acdf SH |
16 | #include <fcntl.h> |
17 | #include <sys/socket.h> | |
18 | #include <netinet/in.h> | |
19 | #include <string.h> | |
5f218239 | 20 | #include <net/if.h> |
aba5acdf SH |
21 | |
22 | #include "libnetlink.h" | |
23 | #include "ll_map.h" | |
4952b459 | 24 | #include "list.h" |
aba5acdf | 25 | |
0025e5d6 SH |
26 | struct ll_cache { |
27 | struct hlist_node idx_hash; | |
28 | struct hlist_node name_hash; | |
aba5acdf | 29 | unsigned flags; |
656111b2 | 30 | unsigned index; |
4b3385f6 | 31 | unsigned short type; |
4b9e9178 | 32 | char name[]; |
aba5acdf SH |
33 | }; |
34 | ||
1e21ea71 | 35 | #define IDXMAP_SIZE 1024 |
0025e5d6 SH |
36 | static struct hlist_head idx_head[IDXMAP_SIZE]; |
37 | static struct hlist_head name_head[IDXMAP_SIZE]; | |
4b3385f6 | 38 | |
0025e5d6 | 39 | static struct ll_cache *ll_get_by_index(unsigned index) |
4b3385f6 | 40 | { |
0025e5d6 SH |
41 | struct hlist_node *n; |
42 | unsigned h = index & (IDXMAP_SIZE - 1); | |
43 | ||
44 | hlist_for_each(n, &idx_head[h]) { | |
45 | struct ll_cache *im | |
46 | = container_of(n, struct ll_cache, idx_hash); | |
47 | if (im->index == index) | |
48 | return im; | |
49 | } | |
50 | ||
51 | return NULL; | |
52 | } | |
53 | ||
d652ccbf | 54 | unsigned namehash(const char *str) |
0025e5d6 SH |
55 | { |
56 | unsigned hash = 5381; | |
57 | ||
58 | while (*str) | |
59 | hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */ | |
60 | ||
61 | return hash; | |
62 | } | |
63 | ||
64 | static struct ll_cache *ll_get_by_name(const char *name) | |
65 | { | |
66 | struct hlist_node *n; | |
67 | unsigned h = namehash(name) & (IDXMAP_SIZE - 1); | |
68 | ||
69 | hlist_for_each(n, &name_head[h]) { | |
70 | struct ll_cache *im | |
71 | = container_of(n, struct ll_cache, name_hash); | |
72 | ||
73 | if (strncmp(im->name, name, IFNAMSIZ) == 0) | |
74 | return im; | |
75 | } | |
76 | ||
77 | return NULL; | |
4b3385f6 | 78 | } |
aba5acdf | 79 | |
cd554f2c | 80 | int ll_remember_index(struct nlmsghdr *n, void *arg) |
aba5acdf | 81 | { |
0025e5d6 SH |
82 | unsigned int h; |
83 | const char *ifname; | |
aba5acdf | 84 | struct ifinfomsg *ifi = NLMSG_DATA(n); |
0025e5d6 | 85 | struct ll_cache *im; |
aba5acdf SH |
86 | struct rtattr *tb[IFLA_MAX+1]; |
87 | ||
0025e5d6 | 88 | if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) |
aba5acdf SH |
89 | return 0; |
90 | ||
6cf2609d | 91 | if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) |
aba5acdf SH |
92 | return -1; |
93 | ||
0025e5d6 SH |
94 | im = ll_get_by_index(ifi->ifi_index); |
95 | if (n->nlmsg_type == RTM_DELLINK) { | |
96 | if (im) { | |
97 | hlist_del(&im->name_hash); | |
98 | hlist_del(&im->idx_hash); | |
99 | free(im); | |
100 | } | |
101 | return 0; | |
102 | } | |
103 | ||
aba5acdf | 104 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); |
0025e5d6 SH |
105 | ifname = rta_getattr_str(tb[IFLA_IFNAME]); |
106 | if (ifname == NULL) | |
aba5acdf SH |
107 | return 0; |
108 | ||
0025e5d6 SH |
109 | if (im) { |
110 | /* change to existing entry */ | |
111 | if (strcmp(im->name, ifname) != 0) { | |
112 | hlist_del(&im->name_hash); | |
113 | h = namehash(ifname) & (IDXMAP_SIZE - 1); | |
114 | hlist_add_head(&im->name_hash, &name_head[h]); | |
115 | } | |
116 | ||
117 | im->flags = ifi->ifi_flags; | |
118 | return 0; | |
aba5acdf SH |
119 | } |
120 | ||
4b9e9178 | 121 | im = malloc(sizeof(*im) + strlen(ifname) + 1); |
0025e5d6 SH |
122 | if (im == NULL) |
123 | return 0; | |
124 | im->index = ifi->ifi_index; | |
125 | strcpy(im->name, ifname); | |
aba5acdf SH |
126 | im->type = ifi->ifi_type; |
127 | im->flags = ifi->ifi_flags; | |
0025e5d6 SH |
128 | |
129 | h = ifi->ifi_index & (IDXMAP_SIZE - 1); | |
130 | hlist_add_head(&im->idx_hash, &idx_head[h]); | |
131 | ||
132 | h = namehash(ifname) & (IDXMAP_SIZE - 1); | |
133 | hlist_add_head(&im->name_hash, &name_head[h]); | |
134 | ||
aba5acdf SH |
135 | return 0; |
136 | } | |
137 | ||
fe269b6e | 138 | const char *ll_idx_n2a(unsigned int idx) |
aba5acdf | 139 | { |
fe269b6e SP |
140 | static char buf[IFNAMSIZ]; |
141 | ||
142 | snprintf(buf, sizeof(buf), "if%u", idx); | |
143 | return buf; | |
144 | } | |
145 | ||
cc5b7e37 | 146 | static unsigned int ll_idx_a2n(const char *name) |
fe269b6e SP |
147 | { |
148 | unsigned int idx; | |
149 | ||
150 | if (sscanf(name, "if%u", &idx) != 1) | |
151 | return 0; | |
152 | return idx; | |
153 | } | |
154 | ||
55870dfe DA |
155 | static int ll_link_get(const char *name, int index) |
156 | { | |
157 | struct { | |
158 | struct nlmsghdr n; | |
159 | struct ifinfomsg ifm; | |
160 | char buf[1024]; | |
161 | } req = { | |
162 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
163 | .n.nlmsg_flags = NLM_F_REQUEST, | |
164 | .n.nlmsg_type = RTM_GETLINK, | |
165 | .ifm.ifi_index = index, | |
166 | }; | |
167 | __u32 filt_mask = RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS; | |
168 | struct rtnl_handle rth = {}; | |
169 | struct nlmsghdr *answer; | |
170 | int rc = 0; | |
171 | ||
172 | if (rtnl_open(&rth, 0) < 0) | |
173 | return 0; | |
174 | ||
175 | addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); | |
176 | if (name) | |
177 | addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, | |
178 | strlen(name) + 1); | |
179 | ||
75783723 | 180 | if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0) |
55870dfe DA |
181 | goto out; |
182 | ||
183 | /* add entry to cache */ | |
184 | rc = ll_remember_index(answer, NULL); | |
185 | if (!rc) { | |
186 | struct ifinfomsg *ifm = NLMSG_DATA(answer); | |
187 | ||
188 | rc = ifm->ifi_index; | |
189 | } | |
190 | ||
191 | free(answer); | |
192 | out: | |
193 | rtnl_close(&rth); | |
194 | return rc; | |
195 | } | |
196 | ||
fe269b6e SP |
197 | const char *ll_index_to_name(unsigned int idx) |
198 | { | |
199 | static char buf[IFNAMSIZ]; | |
4b3385f6 | 200 | const struct ll_cache *im; |
aba5acdf SH |
201 | |
202 | if (idx == 0) | |
203 | return "*"; | |
1e21ea71 | 204 | |
0025e5d6 SH |
205 | im = ll_get_by_index(idx); |
206 | if (im) | |
207 | return im->name; | |
208 | ||
55870dfe DA |
209 | if (ll_link_get(NULL, idx) == idx) { |
210 | im = ll_get_by_index(idx); | |
211 | if (im) | |
212 | return im->name; | |
213 | } | |
214 | ||
0025e5d6 | 215 | if (if_indextoname(idx, buf) == NULL) |
fe269b6e | 216 | snprintf(buf, IFNAMSIZ, "if%u", idx); |
4b3385f6 | 217 | |
aba5acdf SH |
218 | return buf; |
219 | } | |
220 | ||
99f830de | 221 | int ll_index_to_type(unsigned idx) |
aba5acdf | 222 | { |
4b3385f6 | 223 | const struct ll_cache *im; |
aba5acdf SH |
224 | |
225 | if (idx == 0) | |
226 | return -1; | |
0025e5d6 SH |
227 | |
228 | im = ll_get_by_index(idx); | |
229 | return im ? im->type : -1; | |
aba5acdf SH |
230 | } |
231 | ||
656111b2 | 232 | int ll_index_to_flags(unsigned idx) |
aba5acdf | 233 | { |
4b3385f6 | 234 | const struct ll_cache *im; |
aba5acdf SH |
235 | |
236 | if (idx == 0) | |
237 | return 0; | |
238 | ||
0025e5d6 SH |
239 | im = ll_get_by_index(idx); |
240 | return im ? im->flags : -1; | |
aba5acdf SH |
241 | } |
242 | ||
99f830de | 243 | unsigned ll_name_to_index(const char *name) |
aba5acdf | 244 | { |
0025e5d6 | 245 | const struct ll_cache *im; |
24abb62e | 246 | unsigned idx; |
aba5acdf SH |
247 | |
248 | if (name == NULL) | |
249 | return 0; | |
4b3385f6 | 250 | |
0025e5d6 SH |
251 | im = ll_get_by_name(name); |
252 | if (im) | |
253 | return im->index; | |
99f830de | 254 | |
55870dfe DA |
255 | idx = ll_link_get(name, 0); |
256 | if (idx == 0) | |
257 | idx = if_nametoindex(name); | |
24abb62e | 258 | if (idx == 0) |
fe269b6e | 259 | idx = ll_idx_a2n(name); |
24abb62e | 260 | return idx; |
aba5acdf SH |
261 | } |
262 | ||
25c6339b DA |
263 | void ll_drop_by_index(unsigned index) |
264 | { | |
265 | struct ll_cache *im; | |
266 | ||
267 | im = ll_get_by_index(index); | |
268 | if (!im) | |
269 | return; | |
270 | ||
271 | hlist_del(&im->idx_hash); | |
272 | hlist_del(&im->name_hash); | |
273 | ||
274 | free(im); | |
275 | } | |
276 | ||
0025e5d6 | 277 | void ll_init_map(struct rtnl_handle *rth) |
aba5acdf | 278 | { |
30564237 OP |
279 | static int initialized; |
280 | ||
281 | if (initialized) | |
0025e5d6 | 282 | return; |
30564237 | 283 | |
31ae2912 | 284 | if (rtnl_linkdump_req(rth, AF_UNSPEC) < 0) { |
aba5acdf SH |
285 | perror("Cannot send dump request"); |
286 | exit(1); | |
287 | } | |
288 | ||
cd70f3f5 | 289 | if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) { |
aba5acdf SH |
290 | fprintf(stderr, "Dump terminated\n"); |
291 | exit(1); | |
292 | } | |
30564237 OP |
293 | |
294 | initialized = 1; | |
aba5acdf | 295 | } |