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