1/*
2 *   $Id: device-linux.c,v 1.25 2008/01/24 17:08:46 psavola Exp $
3 *
4 *   Authors:
5 *    Lars Fenneberg		<lf@elemental.net>
6 *
7 *   This software is Copyright 1996,1997 by the above mentioned author(s),
8 *   All Rights Reserved.
9 *
10 *   The license which is distributed with this software in the file COPYRIGHT
11 *   applies to this software. If your distribution is missing this file, you
12 *   may request it from <pekkas@netcore.fi>.
13 *
14 */
15
16#include <config.h>
17#include <includes.h>
18#include <radvd.h>
19#include <defaults.h>
20#include <pathnames.h>		/* for PATH_PROC_NET_IF_INET6 */
21
22#ifndef IPV6_ADDR_LINKLOCAL
23#define IPV6_ADDR_LINKLOCAL   0x0020U
24#endif
25
26/*
27 * this function gets the hardware type and address of an interface,
28 * determines the link layer token length and checks it against
29 * the defined prefixes
30 */
31int
32setup_deviceinfo(int sock, struct Interface *iface)
33{
34	struct ifreq	ifr;
35	struct AdvPrefix *prefix;
36	char zero[sizeof(iface->if_addr)];
37
38	strncpy(ifr.ifr_name, iface->Name, IFNAMSIZ-1);
39	ifr.ifr_name[IFNAMSIZ-1] = '\0';
40
41	if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
42		flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed for %s: %s",
43			iface->Name, strerror(errno));
44		return (-1);
45	}
46
47	dlog(LOG_DEBUG, 3, "mtu for %s is %d", iface->Name, ifr.ifr_mtu);
48	iface->if_maxmtu = ifr.ifr_mtu;
49
50	if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
51	{
52		flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed for %s: %s",
53			iface->Name, strerror(errno));
54		return (-1);
55	}
56
57	dlog(LOG_DEBUG, 3, "hardware type for %s is %d", iface->Name,
58		ifr.ifr_hwaddr.sa_family);
59
60	switch(ifr.ifr_hwaddr.sa_family)
61        {
62	case ARPHRD_ETHER:
63		iface->if_hwaddr_len = 48;
64		iface->if_prefix_len = 64;
65		break;
66#ifdef ARPHRD_FDDI
67	case ARPHRD_FDDI:
68		iface->if_hwaddr_len = 48;
69		iface->if_prefix_len = 64;
70		break;
71#endif /* ARPHDR_FDDI */
72#ifdef ARPHRD_ARCNET
73	case ARPHRD_ARCNET:
74		iface->if_hwaddr_len = 8;
75		iface->if_prefix_len = -1;
76		iface->if_maxmtu = -1;
77		break;
78#endif /* ARPHDR_ARCNET */
79	default:
80		iface->if_hwaddr_len = -1;
81		iface->if_prefix_len = -1;
82		iface->if_maxmtu = -1;
83		break;
84	}
85
86	dlog(LOG_DEBUG, 3, "link layer token length for %s is %d", iface->Name,
87		iface->if_hwaddr_len);
88
89	dlog(LOG_DEBUG, 3, "prefix length for %s is %d", iface->Name,
90		iface->if_prefix_len);
91
92	if (iface->if_hwaddr_len != -1) {
93		unsigned int if_hwaddr_len_bytes = (iface->if_hwaddr_len + 7) >> 3;
94
95		if (if_hwaddr_len_bytes > sizeof(iface->if_hwaddr)) {
96			flog(LOG_ERR, "address length %d too big for %s", if_hwaddr_len_bytes, iface->Name);
97			return(-2);
98		}
99		memcpy(iface->if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes);
100
101		memset(zero, 0, sizeof(zero));
102		if (!memcmp(iface->if_hwaddr, zero, if_hwaddr_len_bytes))
103			flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!",
104				iface->Name);
105	}
106
107	prefix = iface->AdvPrefixList;
108	while (prefix)
109	{
110		if ((iface->if_prefix_len != -1) &&
111		   (iface->if_prefix_len != prefix->PrefixLen))
112		{
113			flog(LOG_WARNING, "prefix length should be %d for %s",
114				iface->if_prefix_len, iface->Name);
115 		}
116
117 		prefix = prefix->next;
118	}
119
120	return (0);
121}
122
123/*
124 * this function extracts the link local address and interface index
125 * from PATH_PROC_NET_IF_INET6.  Note: 'sock' unused in Linux.
126 */
127int setup_linklocal_addr(int sock, struct Interface *iface)
128{
129	FILE *fp;
130	char str_addr[40];
131	unsigned int plen, scope, dad_status, if_idx;
132	char devname[IFNAMSIZ];
133
134	if ((fp = fopen(PATH_PROC_NET_IF_INET6, "r")) == NULL)
135	{
136		flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IF_INET6,
137			strerror(errno));
138		return (-1);
139	}
140
141	while (fscanf(fp, "%32s %x %02x %02x %02x %15s\n",
142		      str_addr, &if_idx, &plen, &scope, &dad_status,
143		      devname) != EOF)
144	{
145		if (scope == IPV6_ADDR_LINKLOCAL &&
146		    strcmp(devname, iface->Name) == 0)
147		{
148			struct in6_addr addr;
149			unsigned int ap;
150			int i;
151
152			for (i=0; i<16; i++)
153			{
154				sscanf(str_addr + i * 2, "%02x", &ap);
155				addr.s6_addr[i] = (unsigned char)ap;
156			}
157			memcpy(&iface->if_addr, &addr, sizeof(iface->if_addr));
158
159			iface->if_index = if_idx;
160			fclose(fp);
161			return 0;
162		}
163	}
164
165	flog(LOG_ERR, "no linklocal address configured for %s", iface->Name);
166	fclose(fp);
167	return (-1);
168}
169
170int setup_allrouters_membership(int sock, struct Interface *iface)
171{
172	struct ipv6_mreq mreq;
173
174	memset(&mreq, 0, sizeof(mreq));
175	mreq.ipv6mr_interface = iface->if_index;
176
177	/* ipv6-allrouters: ff02::2 */
178	mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000);
179	mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2);
180
181	if (setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
182	{
183		/* linux-2.6.12-bk4 returns error with HUP signal but keep listening */
184		if (errno != EADDRINUSE)
185		{
186			flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->Name);
187			return (-1);
188		}
189	}
190
191	return (0);
192}
193
194int check_allrouters_membership(int sock, struct Interface *iface)
195{
196	#define ALL_ROUTERS_MCAST "ff020000000000000000000000000002"
197
198	FILE *fp;
199	unsigned int if_idx, allrouters_ok=0;
200	char addr[32+1];
201	int ret=0;
202
203	if ((fp = fopen(PATH_PROC_NET_IGMP6, "r")) == NULL)
204	{
205		flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IGMP6,
206			strerror(errno));
207		return (-1);
208	}
209
210	while ( (ret=fscanf(fp, "%u %*s %32[0-9A-Fa-f] %*x %*x %*x\n", &if_idx, addr)) != EOF) {
211		if (ret == 2) {
212			if (iface->if_index == if_idx) {
213				if (strncmp(addr, ALL_ROUTERS_MCAST, sizeof(addr)) == 0)
214					allrouters_ok = 1;
215			}
216		}
217	}
218
219	fclose(fp);
220
221	if (!allrouters_ok) {
222		flog(LOG_WARNING, "resetting ipv6-allrouters membership on %s", iface->Name);
223		setup_allrouters_membership(sock, iface);
224	}
225
226	return(0);
227}
228
229/* note: also called from the root context */
230int
231set_interface_var(const char *iface,
232		  const char *var, const char *name,
233		  uint32_t val)
234{
235	FILE *fp;
236	char spath[64+IFNAMSIZ];	/* XXX: magic constant */
237	if (snprintf(spath, sizeof(spath), var, iface) >= sizeof(spath))
238		return -1;
239
240	if (access(spath, F_OK) != 0)
241		return -1;
242
243	fp = fopen(spath, "w");
244	if (!fp) {
245		if (name)
246			flog(LOG_ERR, "failed to set %s (%u) for %s: %s",
247			     name, val, iface, strerror(errno));
248		return -1;
249	}
250	fprintf(fp, "%u", val);
251	fclose(fp);
252
253	return 0;
254}
255
256int
257set_interface_linkmtu(const char *iface, uint32_t mtu)
258{
259	if (privsep_enabled())
260		return privsep_interface_linkmtu(iface, mtu);
261
262	return set_interface_var(iface,
263				 PROC_SYS_IP6_LINKMTU, "LinkMTU",
264				 mtu);
265}
266
267int
268set_interface_curhlim(const char *iface, uint8_t hlim)
269{
270	if (privsep_enabled())
271		return privsep_interface_curhlim(iface, hlim);
272
273	return set_interface_var(iface,
274				 PROC_SYS_IP6_CURHLIM, "CurHopLimit",
275				 hlim);
276}
277
278int
279set_interface_reachtime(const char *iface, uint32_t rtime)
280{
281	int ret;
282
283	if (privsep_enabled())
284		return privsep_interface_reachtime(iface, rtime);
285
286	ret = set_interface_var(iface,
287				PROC_SYS_IP6_BASEREACHTIME_MS,
288				NULL,
289				rtime);
290	if (ret)
291		ret = set_interface_var(iface,
292					PROC_SYS_IP6_BASEREACHTIME,
293					"BaseReachableTimer",
294					rtime / 1000); /* sec */
295	return ret;
296}
297
298int
299set_interface_retranstimer(const char *iface, uint32_t rettimer)
300{
301	int ret;
302
303	if (privsep_enabled())
304		return privsep_interface_retranstimer(iface, rettimer);
305
306	ret = set_interface_var(iface,
307				PROC_SYS_IP6_RETRANSTIMER_MS,
308				NULL,
309				rettimer);
310	if (ret)
311		ret = set_interface_var(iface,
312					PROC_SYS_IP6_RETRANSTIMER,
313					"RetransTimer",
314					rettimer / 1000 * USER_HZ); /* XXX user_hz */
315	return ret;
316}
317
318