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