/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "util/platform/netdev/NetPlatform.h" #include "util/platform/Sockaddr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /** * This hack exists because linux/in.h and linux/in6.h define * the same structures, leading to redefinition errors. */ #ifndef _LINUX_IN6_H struct in6_ifreq { struct in6_addr ifr6_addr; uint32_t ifr6_prefixlen; int ifr6_ifindex; }; #endif /** * Get a socket and ifRequest for a given interface by name. * * @param interfaceName the name of the interface, eg: tun0 * @param af either AF_INET or AF_INET6 * @param eg an exception handler in case something goes wrong. * this will send a -1 for all errors. * @param ifRequestOut an ifreq which will be populated with the interface index of the interface. * @return a socket for interacting with this interface. */ static int socketForIfName(const char* interfaceName, int af, struct Except* eh, struct ifreq* ifRequestOut) { int s; if ((s = socket(af, SOCK_DGRAM, 0)) < 0) { Except_throw(eh, "socket() [%s]", strerror(errno)); } memset(ifRequestOut, 0, sizeof(struct ifreq)); strncpy(ifRequestOut->ifr_name, interfaceName, IFNAMSIZ); if (ioctl(s, SIOCGIFINDEX, ifRequestOut) < 0) { int err = errno; close(s); Except_throw(eh, "ioctl(SIOCGIFINDEX) [%s]", strerror(err)); } return s; } static void checkInterfaceUp(int socket, struct ifreq* ifRequest, struct Log* logger, struct Except* eh) { if (ioctl(socket, SIOCGIFFLAGS, ifRequest) < 0) { int err = errno; close(socket); Except_throw(eh, "ioctl(SIOCGIFFLAGS) [%s]", strerror(err)); } if (ifRequest->ifr_flags & IFF_UP & IFF_RUNNING) { // already up. return; } Log_info(logger, "Bringing up interface [%s]", ifRequest->ifr_name); ifRequest->ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(socket, SIOCSIFFLAGS, ifRequest) < 0) { int err = errno; close(socket); Except_throw(eh, "ioctl(SIOCSIFFLAGS) [%s]", strerror(err)); } } void NetPlatform_addAddress(const char* interfaceName, const uint8_t* address, int prefixLen, int addrFam, struct Log* logger, struct Except* eh) { struct ifreq ifRequest; int s = socketForIfName(interfaceName, addrFam, eh, &ifRequest); int ifIndex = ifRequest.ifr_ifindex; // checkInterfaceUp() clobbers the ifindex. checkInterfaceUp(s, &ifRequest, logger, eh); if (addrFam == Sockaddr_AF_INET6) { struct in6_ifreq ifr6 = { .ifr6_ifindex = ifIndex, .ifr6_prefixlen = prefixLen }; memcpy(&ifr6.ifr6_addr, address, 16); if (ioctl(s, SIOCSIFADDR, &ifr6) < 0) { int err = errno; close(s); Except_throw(eh, "ioctl(SIOCSIFADDR) [%s]", strerror(err)); } } else if (addrFam == Sockaddr_AF_INET) { struct sockaddr_in sin = { .sin_family = AF_INET, .sin_port = 0 }; memcpy(&sin.sin_addr.s_addr, address, 4); memcpy(&ifRequest.ifr_addr, &sin, sizeof(struct sockaddr)); if (ioctl(s, SIOCSIFADDR, &ifRequest) < 0) { int err = errno; close(s); Except_throw(eh, "ioctl(SIOCSIFADDR) failed: [%s]", strerror(err)); } uint32_t x = ~0 << (32 - prefixLen); x = Endian_hostToBigEndian32(x); memcpy(&sin.sin_addr, &x, 4); memcpy(&ifRequest.ifr_addr, &sin, sizeof(struct sockaddr_in)); if (ioctl(s, SIOCSIFNETMASK, &ifRequest) < 0) { int err = errno; close(s); Except_throw(eh, "ioctl(SIOCSIFNETMASK) failed: [%s]", strerror(err)); } } else { Assert_true(0); } close(s); } void NetPlatform_setMTU(const char* interfaceName, uint32_t mtu, struct Log* logger, struct Except* eh) { struct ifreq ifRequest; int s = socketForIfName(interfaceName, AF_INET6, eh, &ifRequest); Log_info(logger, "Setting MTU for device [%s] to [%u] bytes.", interfaceName, mtu); ifRequest.ifr_mtu = mtu; if (ioctl(s, SIOCSIFMTU, &ifRequest) < 0) { int err = errno; close(s); Except_throw(eh, "ioctl(SIOCSIFMTU) [%s]", strerror(err)); } close(s); }