/* * $Id: device-linux.c,v 1.21 2006/10/08 19:32:22 psavola Exp $ * * Authors: * Lars Fenneberg * * This software is Copyright 1996,1997 by the above mentioned author(s), * All Rights Reserved. * * The license which is distributed with this software in the file COPYRIGHT * applies to this software. If your distribution is missing this file, you * may request it from . * */ #include #include #include #include #include /* for PATH_PROC_NET_IF_INET6 */ #ifndef IPV6_ADDR_LINKLOCAL #define IPV6_ADDR_LINKLOCAL 0x0020U #endif /* * this function gets the hardware type and address of an interface, * determines the link layer token length and checks it against * the defined prefixes */ int setup_deviceinfo(int sock, struct Interface *iface) { struct ifreq ifr; struct AdvPrefix *prefix; char zero[sizeof(iface->if_addr)]; strncpy(ifr.ifr_name, iface->Name, IFNAMSIZ-1); ifr.ifr_name[IFNAMSIZ-1] = '\0'; if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) { flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed for %s: %s", iface->Name, strerror(errno)); return (-1); } dlog(LOG_DEBUG, 3, "mtu for %s is %d", iface->Name, ifr.ifr_mtu); iface->if_maxmtu = ifr.ifr_mtu; if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) { flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed for %s: %s", iface->Name, strerror(errno)); return (-1); } dlog(LOG_DEBUG, 3, "hardware type for %s is %d", iface->Name, ifr.ifr_hwaddr.sa_family); switch(ifr.ifr_hwaddr.sa_family) { case ARPHRD_ETHER: iface->if_hwaddr_len = 48; iface->if_prefix_len = 64; break; #ifdef ARPHRD_FDDI case ARPHRD_FDDI: iface->if_hwaddr_len = 48; iface->if_prefix_len = 64; break; #endif /* ARPHDR_FDDI */ #ifdef ARPHRD_ARCNET case ARPHRD_ARCNET: iface->if_hwaddr_len = 8; iface->if_prefix_len = -1; iface->if_maxmtu = -1; break; #endif /* ARPHDR_ARCNET */ default: iface->if_hwaddr_len = -1; iface->if_prefix_len = -1; iface->if_maxmtu = -1; break; } dlog(LOG_DEBUG, 3, "link layer token length for %s is %d", iface->Name, iface->if_hwaddr_len); dlog(LOG_DEBUG, 3, "prefix length for %s is %d", iface->Name, iface->if_prefix_len); if (iface->if_hwaddr_len != -1) { unsigned int if_hwaddr_len_bytes = (iface->if_hwaddr_len + 7) >> 3; if (if_hwaddr_len_bytes > sizeof(iface->if_hwaddr)) { flog(LOG_ERR, "address length %d too big for %s", if_hwaddr_len_bytes, iface->Name); return(-2); } memcpy(iface->if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes); memset(zero, 0, sizeof(zero)); if (!memcmp(iface->if_hwaddr, zero, if_hwaddr_len_bytes)) flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!", iface->Name); } prefix = iface->AdvPrefixList; while (prefix) { if ((iface->if_prefix_len != -1) && (iface->if_prefix_len != prefix->PrefixLen)) { flog(LOG_WARNING, "prefix length should be %d for %s", iface->if_prefix_len, iface->Name); } prefix = prefix->next; } return (0); } /* * this function extracts the link local address and interface index * from PATH_PROC_NET_IF_INET6. Note: 'sock' unused in Linux. */ int setup_linklocal_addr(int sock, struct Interface *iface) { FILE *fp; char str_addr[40]; unsigned int plen, scope, dad_status, if_idx; char devname[IFNAMSIZ]; if ((fp = fopen(PATH_PROC_NET_IF_INET6, "r")) == NULL) { flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IF_INET6, strerror(errno)); return (-1); } while (fscanf(fp, "%32s %x %02x %02x %02x %15s\n", str_addr, &if_idx, &plen, &scope, &dad_status, devname) != EOF) { if (scope == IPV6_ADDR_LINKLOCAL && strcmp(devname, iface->Name) == 0) { struct in6_addr addr; unsigned int ap; int i; for (i=0; i<16; i++) { sscanf(str_addr + i * 2, "%02x", &ap); addr.s6_addr[i] = (unsigned char)ap; } memcpy(&iface->if_addr, &addr, sizeof(iface->if_addr)); iface->if_index = if_idx; fclose(fp); return 0; } } flog(LOG_ERR, "no linklocal address configured for %s", iface->Name); fclose(fp); return (-1); } int setup_allrouters_membership(int sock, struct Interface *iface) { struct ipv6_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.ipv6mr_interface = iface->if_index; /* ipv6-allrouters: ff02::2 */ mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000); mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2); if (setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { /* linux-2.6.12-bk4 returns error with HUP signal but keep listening */ if (errno != EADDRINUSE) { flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->Name); return (-1); } } return (0); } int check_allrouters_membership(int sock, struct Interface *iface) { #define ALL_ROUTERS_MCAST "ff020000000000000000000000000002" FILE *fp; unsigned int if_idx, allrouters_ok=0; char addr[32+1]; int ret=0; if ((fp = fopen(PATH_PROC_NET_IGMP6, "r")) == NULL) { flog(LOG_ERR, "can't open %s: %s", PATH_PROC_NET_IGMP6, strerror(errno)); return (-1); } while ( (ret=fscanf(fp, "%u %*s %32[0-9A-Fa-f] %*x %*x %*x\n", &if_idx, addr)) != EOF) { if (ret == 2) { if (iface->if_index == if_idx) { if (strncmp(addr, ALL_ROUTERS_MCAST, sizeof(addr)) == 0) allrouters_ok = 1; } } } fclose(fp); if (!allrouters_ok) { flog(LOG_WARNING, "resetting ipv6-allrouters membership on %s", iface->Name); setup_allrouters_membership(sock, iface); } return(0); } static int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val) { FILE *fp; char spath[64+IFNAMSIZ]; /* XXX: magic constant */ snprintf(spath, sizeof(spath), var, iface); fp = fopen(spath, "w"); if (!fp) { if (name) flog(LOG_ERR, "failed to set %s (%u) for %s", name, val, iface); return -1; } fprintf(fp, "%u", val); fclose(fp); return 0; } int set_interface_linkmtu(const char *iface, uint32_t mtu) { return set_interface_var(iface, PROC_SYS_IP6_LINKMTU, "LinkMTU", mtu); } int set_interface_curhlim(const char *iface, uint8_t hlim) { return set_interface_var(iface, PROC_SYS_IP6_CURHLIM, "CurHopLimit", hlim); } int set_interface_reachtime(const char *iface, uint32_t rtime) { int ret; ret = set_interface_var(iface, PROC_SYS_IP6_BASEREACHTIME_MS, NULL, rtime); if (ret) ret = set_interface_var(iface, PROC_SYS_IP6_BASEREACHTIME, "BaseReachableTimer", rtime / 1000); return ret; } int set_interface_retranstimer(const char *iface, uint32_t rettimer) { int ret; ret = set_interface_var(iface, PROC_SYS_IP6_RETRANSTIMER_MS, NULL, rettimer); if (ret) ret = set_interface_var(iface, PROC_SYS_IP6_RETRANSTIMER, "RetransTimer", rettimer / 1000); return ret; }