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
26extern unsigned int if_nametoindex (const char *);
27
28struct 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
40static struct ll_cache *idx_head[IDXMAP_SIZE];
41
42static inline struct ll_cache *idxhead(int idx)
43{
44	return idx_head[idx & (IDXMAP_SIZE - 1)];
45}
46
47int 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
96const 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
112const char *ll_index_to_name(unsigned idx)
113{
114	static char nbuf[IFNAMSIZ];
115
116	return ll_idx_n2a(idx, nbuf);
117}
118
119int 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
131unsigned 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
144unsigned 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
165unsigned 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
195int 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