Merge branch 'tomato-ND-USBmod' into tomato-RT
[tomato.git] / release / src / router / radvd / device-linux.c
blob3bb0beb6650a20a8bbc333128a44c06f4eba9d85
1 /*
2 * $Id: device-linux.c,v 1.28 2011/02/06 03:41:38 reubenhwk Exp $
4 * Authors:
5 * Lars Fenneberg <lf@elemental.net>
7 * This software is Copyright 1996,1997 by the above mentioned author(s),
8 * All Rights Reserved.
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>.
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 */
22 #ifndef IPV6_ADDR_LINKLOCAL
23 #define IPV6_ADDR_LINKLOCAL 0x0020U
24 #endif
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
31 int
32 setup_deviceinfo(struct Interface *iface)
34 struct ifreq ifr;
35 struct AdvPrefix *prefix;
36 char zero[sizeof(iface->if_addr)];
38 strncpy(ifr.ifr_name, iface->Name, IFNAMSIZ-1);
39 ifr.ifr_name[IFNAMSIZ-1] = '\0';
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);
47 dlog(LOG_DEBUG, 3, "mtu for %s is %d", iface->Name, ifr.ifr_mtu);
48 iface->if_maxmtu = ifr.ifr_mtu;
50 if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
52 flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed for %s: %s",
53 iface->Name, strerror(errno));
54 return (-1);
57 dlog(LOG_DEBUG, 3, "hardware type for %s is %d", iface->Name,
58 ifr.ifr_hwaddr.sa_family);
60 switch(ifr.ifr_hwaddr.sa_family)
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;
86 dlog(LOG_DEBUG, 3, "link layer token length for %s is %d", iface->Name,
87 iface->if_hwaddr_len);
89 dlog(LOG_DEBUG, 3, "prefix length for %s is %d", iface->Name,
90 iface->if_prefix_len);
92 if (iface->if_hwaddr_len != -1) {
93 unsigned int if_hwaddr_len_bytes = (iface->if_hwaddr_len + 7) >> 3;
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);
99 memcpy(iface->if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes);
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);
107 prefix = iface->AdvPrefixList;
108 while (prefix)
110 if ((iface->if_prefix_len != -1) &&
111 (iface->if_prefix_len != prefix->PrefixLen))
113 flog(LOG_WARNING, "prefix length should be %d for %s",
114 iface->if_prefix_len, iface->Name);
117 prefix = prefix->next;
120 return (0);
124 * this function extracts the link local address and interface index
125 * from PATH_PROC_NET_IF_INET6. Note: 'sock' unused in Linux.
127 int setup_linklocal_addr(struct Interface *iface)
129 FILE *fp;
130 char str_addr[40];
131 unsigned int plen, scope, dad_status, if_idx;
132 char devname[IFNAMSIZ];
134 if ((fp = fopen(PATH_PROC_NET_IF_INET6, "r")) == NULL)
136 flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IF_INET6,
137 strerror(errno));
138 return (-1);
141 while (fscanf(fp, "%32s %x %02x %02x %02x %15s\n",
142 str_addr, &if_idx, &plen, &scope, &dad_status,
143 devname) != EOF)
145 if (scope == IPV6_ADDR_LINKLOCAL &&
146 strcmp(devname, iface->Name) == 0)
148 struct in6_addr addr;
149 unsigned int ap;
150 int i;
152 for (i=0; i<16; i++)
154 sscanf(str_addr + i * 2, "%02x", &ap);
155 addr.s6_addr[i] = (unsigned char)ap;
157 memcpy(&iface->if_addr, &addr, sizeof(iface->if_addr));
159 iface->if_index = if_idx;
160 fclose(fp);
161 return 0;
165 flog(LOG_ERR, "no linklocal address configured for %s", iface->Name);
166 fclose(fp);
167 return (-1);
170 int setup_allrouters_membership(struct Interface *iface)
172 struct ipv6_mreq mreq;
174 memset(&mreq, 0, sizeof(mreq));
175 mreq.ipv6mr_interface = iface->if_index;
177 /* ipv6-allrouters: ff02::2 */
178 mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000);
179 mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2);
181 if (setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
183 /* linux-2.6.12-bk4 returns error with HUP signal but keep listening */
184 if (errno != EADDRINUSE)
186 flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->Name);
187 return (-1);
191 return (0);
194 int check_allrouters_membership(struct Interface *iface)
196 #define ALL_ROUTERS_MCAST "ff020000000000000000000000000002"
198 FILE *fp;
199 unsigned int if_idx, allrouters_ok=0;
200 char addr[32+1];
201 char buffer[301] = {""}, *str;
202 int ret=0;
204 if ((fp = fopen(PATH_PROC_NET_IGMP6, "r")) == NULL)
206 flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IGMP6,
207 strerror(errno));
208 return (-1);
211 str = fgets(buffer, 300, fp);
213 while (str && (ret = sscanf(str, "%u %*s %32[0-9A-Fa-f]", &if_idx, addr)) ) {
214 if (ret == 2) {
215 if (iface->if_index == if_idx) {
216 if (strncmp(addr, ALL_ROUTERS_MCAST, sizeof(addr)) == 0){
217 allrouters_ok = 1;
218 break;
222 str = fgets(buffer, 300, fp);
225 fclose(fp);
227 if (!allrouters_ok) {
228 flog(LOG_WARNING, "resetting ipv6-allrouters membership on %s", iface->Name);
229 setup_allrouters_membership(iface);
232 return(0);
235 /* note: also called from the root context */
237 set_interface_var(const char *iface,
238 const char *var, const char *name,
239 uint32_t val)
241 FILE *fp;
242 char spath[64+IFNAMSIZ]; /* XXX: magic constant */
243 if (snprintf(spath, sizeof(spath), var, iface) >= sizeof(spath))
244 return -1;
246 if (access(spath, F_OK) != 0)
247 return -1;
249 fp = fopen(spath, "w");
250 if (!fp) {
251 if (name)
252 flog(LOG_ERR, "failed to set %s (%u) for %s: %s",
253 name, val, iface, strerror(errno));
254 return -1;
256 fprintf(fp, "%u", val);
257 fclose(fp);
259 return 0;
263 set_interface_linkmtu(const char *iface, uint32_t mtu)
265 if (privsep_enabled())
266 return privsep_interface_linkmtu(iface, mtu);
268 return set_interface_var(iface,
269 PROC_SYS_IP6_LINKMTU, "LinkMTU",
270 mtu);
274 set_interface_curhlim(const char *iface, uint8_t hlim)
276 if (privsep_enabled())
277 return privsep_interface_curhlim(iface, hlim);
279 return set_interface_var(iface,
280 PROC_SYS_IP6_CURHLIM, "CurHopLimit",
281 hlim);
285 set_interface_reachtime(const char *iface, uint32_t rtime)
287 int ret;
289 if (privsep_enabled())
290 return privsep_interface_reachtime(iface, rtime);
292 ret = set_interface_var(iface,
293 PROC_SYS_IP6_BASEREACHTIME_MS,
294 NULL,
295 rtime);
296 if (ret)
297 ret = set_interface_var(iface,
298 PROC_SYS_IP6_BASEREACHTIME,
299 "BaseReachableTimer",
300 rtime / 1000); /* sec */
301 return ret;
305 set_interface_retranstimer(const char *iface, uint32_t rettimer)
307 int ret;
309 if (privsep_enabled())
310 return privsep_interface_retranstimer(iface, rettimer);
312 ret = set_interface_var(iface,
313 PROC_SYS_IP6_RETRANSTIMER_MS,
314 NULL,
315 rettimer);
316 if (ret)
317 ret = set_interface_var(iface,
318 PROC_SYS_IP6_RETRANSTIMER,
319 "RetransTimer",
320 rettimer / 1000 * USER_HZ); /* XXX user_hz */
321 return ret;