/* 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 "exception/Err.h" #include "util/platform/netdev/NetPlatform.h" #include "util/Assert.h" #include "util/platform/Sockaddr.h" #include "util/CString.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void maskForPrefix(uint8_t mask[16], int prefix) { for (int i = 0; i < 16; i += 8) { if (i + 8 <= prefix) { mask[i] = 0xff; } else if (i > prefix) { mask[i] = 0x00; } else { mask[i] = (0xff << (i + 8 - prefix)) & 0xff; } } } struct RouteMessage { struct rt_msghdr header; struct sockaddr_in6 dest; struct sockaddr_in6 gateway; struct sockaddr_in6 netmask; }; Assert_compileTime(sizeof(struct RouteMessage) == 172); static Err_DEFUN setupRoute(const uint8_t address[16], int prefixLen, struct Log* logger, struct Allocator* alloc) { struct RouteMessage rm = { .header = { .rtm_type = RTM_ADD, .rtm_version = RTM_VERSION, .rtm_seq = 0, .rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK, .rtm_msglen = sizeof(struct RouteMessage) }, .dest = { .sin6_family = AF_INET6 }, .gateway = { .sin6_family = AF_INET6 }, .netmask = { .sin6_family = AF_INET6 } }; memcpy(&rm.dest.sin6_addr, address, 16); memcpy(&rm.gateway.sin6_addr, address, 16); maskForPrefix((uint8_t*) &rm.netmask.sin6_addr, prefixLen); int sock = socket(PF_ROUTE, SOCK_RAW, 0); if (sock == -1) { Err_raise(alloc, "open route socket [%s]", strerror(errno)); } ssize_t returnLen = write(sock, (char*) &rm, rm.header.rtm_msglen); if (returnLen < 0) { Err_raise(alloc, "insert route [%s]", strerror(errno)); } else if (returnLen < rm.header.rtm_msglen) { Err_raise(alloc, "insert route returned only [%d] of [%d]", (int)returnLen, rm.header.rtm_msglen); } return NULL; } static Err_DEFUN addIp4Address(const char* interfaceName, const uint8_t address[4], int prefixLen, struct Log* logger, struct Allocator* alloc) { // TODO(cjd): implement this and then remove the exception from TUNInterface_ipv4_root_test.c Err_raise(alloc, "unimplemented"); } static Err_DEFUN addIp6Address(const char* interfaceName, const uint8_t address[16], int prefixLen, struct Log* logger, struct Allocator* alloc) { struct lifreq ifr = { .lifr_ppa = 0, .lifr_flags = 0 }; char* error = NULL; struct sockaddr_in6* sin6 = (struct sockaddr_in6 *) &ifr.lifr_addr; maskForPrefix((uint8_t*) sin6->sin6_addr.s6_addr, prefixLen); ifr.lifr_addr.ss_family = AF_INET6; CString_safeStrncpy(ifr.lifr_name, interfaceName, LIFNAMSIZ); int udpSock = socket(AF_INET6, SOCK_DGRAM, 0); if (udpSock < 0) { error = "socket(AF_INET6, SOCK_DGRAM, 0)"; } else if (ioctl(udpSock, SIOCSLIFNETMASK, (caddr_t)&ifr) < 0) { // set the netmask. error = "ioctl(SIOCSLIFNETMASK) (setting netmask)"; } memcpy(&sin6->sin6_addr, address, 16); if (!error && ioctl(udpSock, SIOCSLIFADDR, (caddr_t)&ifr) < 0) { // set the ip address. error = "ioctl(SIOCSLIFADDR) (setting ipv6 address)"; } if (!error && ioctl(udpSock, SIOCSLIFDSTADDR, (caddr_t)&ifr) < 0) { // set the destination address for the point-to-point connection // use the same as the source address since we're not really using it. error = "ioctl(SIOCGLIFDSTADDR) (setting point-to-point destination address)"; } memset(sin6, 0, sizeof(struct sockaddr_in6)); if (!error && ioctl(udpSock, SIOCGLIFFLAGS, (caddr_t)&ifr) < 0) { // get the flags for the device. error = "ioctl(SIOCGLIFFLAGS) (getting device flags)"; } ifr.lifr_flags |= IFF_UP; if (!error && ioctl(udpSock, SIOCSLIFFLAGS, (caddr_t)&ifr) < 0) { // bring the interface up error = "ioctl(SIOCSLIFFLAGS) (bringing the interface up)"; } if (error) { int err = errno; close(udpSock); Err_raise(alloc, "%s [%s]", error, strerror(err)); } close(udpSock); Err(setupRoute(address, prefixLen, logger, alloc)); return NULL; } Err_DEFUN NetPlatform_addAddress(const char* interfaceName, const uint8_t* address, int prefixLen, int addrFam, struct Log* logger, struct Allocator* tempAlloc) { if (addrFam == AF_INET6) { return addIp6Address(interfaceName, address, prefixLen, logger, tempAlloc); } else if (addrFam == AF_INET) { return addIp4Address(interfaceName, address, prefixLen, logger, tempAlloc); } else { Err_raise(tempAlloc, "Invalid address family [%d]", addrFam); } } Err_DEFUN NetPlatform_setMTU(const char* interfaceName, uint32_t mtu, struct Log* logger, struct Allocator* errAlloc) { Err_raise(errAlloc, "Not implemented in Illumos"); } Err_DEFUN NetPlatform_setRoutes(const char* ifName, struct Sockaddr** prefixSet, int prefixCount, struct Log* logger, struct Allocator* tempAlloc) { Err_raise(tempAlloc, "NetPlatform_setRoutes is not implemented in this platform."); }