1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * This file contains the functions that are required for communicating 28 * with in.ndpd while creating autoconfigured addresses. 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <unistd.h> 38 #include <sys/sockio.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 #include <inet/ip.h> 44 #include <arpa/inet.h> 45 #include <assert.h> 46 #include <poll.h> 47 #include <ipadm_ndpd.h> 48 #include "libipadm_impl.h" 49 50 #define NDPDTIMEOUT 5000 51 #define PREFIXLEN_LINKLOCAL 10 52 53 static ipadm_status_t i_ipadm_create_linklocal(ipadm_handle_t, 54 ipadm_addrobj_t); 55 static void i_ipadm_make_linklocal(struct sockaddr_in6 *, 56 const struct in6_addr *); 57 static ipadm_status_t i_ipadm_send_ndpd_cmd(const char *, 58 const struct ipadm_addrobj_s *, int); 59 60 /* 61 * Sends message to in.ndpd asking not to do autoconf for the given interface, 62 * until IPADM_CREATE_ADDRS or IPADM_ENABLE_AUTOCONF is sent. 63 */ 64 ipadm_status_t 65 i_ipadm_disable_autoconf(const char *ifname) 66 { 67 return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_DISABLE_AUTOCONF)); 68 } 69 70 /* 71 * Sends message to in.ndpd to enable autoconf for the given interface, 72 * until another IPADM_DISABLE_AUTOCONF is sent. 73 */ 74 ipadm_status_t 75 i_ipadm_enable_autoconf(const char *ifname) 76 { 77 return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_ENABLE_AUTOCONF)); 78 } 79 80 ipadm_status_t 81 i_ipadm_create_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t addr, 82 uint32_t i_flags) 83 { 84 ipadm_status_t status; 85 86 /* 87 * Create the link local based on the given token. If the same intfid 88 * was already used with a different address object, this step will 89 * fail. 90 */ 91 status = i_ipadm_create_linklocal(iph, addr); 92 if (status != IPADM_SUCCESS) 93 return (status); 94 95 /* 96 * Request in.ndpd to start the autoconfiguration. 97 * If autoconfiguration was already started by another means (e.g. 98 * "ifconfig" ), in.ndpd will return EEXIST. 99 */ 100 if (addr->ipadm_stateless || addr->ipadm_stateful) { 101 status = i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr, 102 IPADM_CREATE_ADDRS); 103 if (status != IPADM_SUCCESS && 104 status != IPADM_NDPD_NOT_RUNNING) { 105 (void) i_ipadm_delete_addr(iph, addr); 106 return (status); 107 } 108 } 109 110 /* Persist the intfid. */ 111 status = i_ipadm_addr_persist(iph, addr, B_FALSE, i_flags); 112 if (status != IPADM_SUCCESS) { 113 (void) i_ipadm_delete_addr(iph, addr); 114 (void) i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr, 115 IPADM_DELETE_ADDRS); 116 } 117 118 return (status); 119 } 120 121 ipadm_status_t 122 i_ipadm_delete_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t ipaddr) 123 { 124 ipadm_status_t status; 125 126 /* 127 * Send a msg to in.ndpd to remove the autoconfigured addresses, 128 * and delete the link local that was created. 129 */ 130 status = i_ipadm_send_ndpd_cmd(ipaddr->ipadm_ifname, ipaddr, 131 IPADM_DELETE_ADDRS); 132 if (status == IPADM_NDPD_NOT_RUNNING) 133 status = IPADM_SUCCESS; 134 if (status == IPADM_SUCCESS) 135 status = i_ipadm_delete_addr(iph, ipaddr); 136 137 return (status); 138 } 139 140 static ipadm_status_t 141 i_ipadm_create_linklocal(ipadm_handle_t iph, ipadm_addrobj_t addr) 142 { 143 boolean_t addif = B_FALSE; 144 struct sockaddr_in6 *sin6; 145 struct lifreq lifr; 146 int err; 147 ipadm_status_t status; 148 in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 149 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; 150 151 bzero(&lifr, sizeof (lifr)); 152 (void) strlcpy(lifr.lifr_name, addr->ipadm_ifname, LIFNAMSIZ); 153 154 if ((err = ioctl(iph->iph_sock6, SIOCGLIFADDR, (caddr_t)&lifr)) < 0) 155 return (ipadm_errno2status(errno)); 156 157 /* 158 * If no address exists on 0th logical interface, 159 * create link-local address on it. Else, create a new 160 * logical interface. 161 */ 162 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 163 if (!IN6_IS_ADDR_UNSPECIFIED((&sin6->sin6_addr))) { 164 if (ioctl(iph->iph_sock6, SIOCLIFADDIF, (caddr_t)&lifr) < 0) 165 return (ipadm_errno2status(errno)); 166 addr->ipadm_lifnum = i_ipadm_get_lnum(lifr.lifr_name); 167 addif = B_TRUE; 168 } 169 /* Create the link-local address */ 170 bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr)); 171 (void) plen2mask(PREFIXLEN_LINKLOCAL, AF_INET6, &lifr.lifr_addr); 172 if ((err = ioctl(iph->iph_sock6, SIOCSLIFNETMASK, (caddr_t)&lifr)) < 0) 173 goto fail; 174 if (addr->ipadm_intfidlen == 0) { 175 /* 176 * If we have to use the default interface id, 177 * we just need to set the prefix to the link-local prefix. 178 * SIOCSLIFPREFIX sets the address with the given prefix 179 * and the default interface id. 180 */ 181 sin6->sin6_addr = ll_template; 182 err = ioctl(iph->iph_sock6, SIOCSLIFPREFIX, (caddr_t)&lifr); 183 if (err < 0) 184 goto fail; 185 } else { 186 /* Make a linklocal address in sin6 and set it */ 187 i_ipadm_make_linklocal(sin6, &addr->ipadm_intfid.sin6_addr); 188 err = ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr); 189 if (err < 0) 190 goto fail; 191 } 192 if ((err = ioctl(iph->iph_sock6, SIOCGLIFFLAGS, (char *)&lifr)) < 0) 193 goto fail; 194 lifr.lifr_flags |= IFF_UP; 195 if ((err = ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (char *)&lifr)) < 0) 196 goto fail; 197 return (IPADM_SUCCESS); 198 199 fail: 200 if (errno == EEXIST) 201 status = IPADM_ADDRCONF_EXISTS; 202 else 203 status = ipadm_errno2status(errno); 204 /* Remove the linklocal that was created. */ 205 if (addif) { 206 (void) ioctl(iph->iph_sock6, SIOCLIFREMOVEIF, (caddr_t)&lifr); 207 } else { 208 struct sockaddr_in6 *sin6; 209 210 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 211 lifr.lifr_flags &= ~IFF_UP; 212 (void) ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (caddr_t)&lifr); 213 sin6->sin6_family = AF_INET6; 214 sin6->sin6_addr = in6addr_any; 215 (void) ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr); 216 } 217 return (status); 218 } 219 220 /* 221 * Make a linklocal address based on the given intfid and copy it into 222 * the output parameter `sin6'. 223 */ 224 static void 225 i_ipadm_make_linklocal(struct sockaddr_in6 *sin6, const struct in6_addr *intfid) 226 { 227 int i; 228 in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 229 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; 230 231 sin6->sin6_family = AF_INET6; 232 sin6->sin6_addr = *intfid; 233 for (i = 0; i < 4; i++) { 234 sin6->sin6_addr.s6_addr[i] = 235 sin6->sin6_addr.s6_addr[i] | ll_template.s6_addr[i]; 236 } 237 } 238 239 /* 240 * Function that forms an ndpd msg and sends it to the in.ndpd daemon's loopback 241 * listener socket. 242 */ 243 static ipadm_status_t 244 i_ipadm_send_ndpd_cmd(const char *ifname, const struct ipadm_addrobj_s *addr, 245 int cmd) 246 { 247 int fd; 248 struct sockaddr_un servaddr; 249 int flags; 250 ipadm_ndpd_msg_t msg; 251 int retval; 252 253 if (addr == NULL && 254 (cmd == IPADM_CREATE_ADDRS || cmd == IPADM_DELETE_ADDRS)) { 255 return (IPADM_INVALID_ARG); 256 } 257 258 fd = socket(AF_UNIX, SOCK_STREAM, 0); 259 if (fd == -1) 260 return (IPADM_FAILURE); 261 262 /* Put the socket in non-blocking mode */ 263 flags = fcntl(fd, F_GETFL, 0); 264 if (flags != -1) 265 (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK); 266 267 /* Connect to in.ndpd */ 268 bzero(&servaddr, sizeof (servaddr)); 269 servaddr.sun_family = AF_UNIX; 270 (void) strlcpy(servaddr.sun_path, IPADM_UDS_PATH, 271 sizeof (servaddr.sun_path)); 272 if (connect(fd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1) 273 goto fail; 274 275 bzero(&msg, sizeof (msg)); 276 msg.inm_cmd = cmd; 277 (void) strlcpy(msg.inm_ifname, ifname, sizeof (msg.inm_ifname)); 278 if (addr != NULL) { 279 msg.inm_intfid = addr->ipadm_intfid; 280 msg.inm_intfidlen = addr->ipadm_intfidlen; 281 msg.inm_stateless = addr->ipadm_stateless; 282 msg.inm_stateful = addr->ipadm_stateful; 283 if (cmd == IPADM_CREATE_ADDRS) { 284 (void) strlcpy(msg.inm_aobjname, addr->ipadm_aobjname, 285 sizeof (msg.inm_aobjname)); 286 } 287 } 288 if (ipadm_ndpd_write(fd, &msg, sizeof (msg)) < 0) 289 goto fail; 290 if (ipadm_ndpd_read(fd, &retval, sizeof (retval)) < 0) 291 goto fail; 292 (void) close(fd); 293 if (cmd == IPADM_CREATE_ADDRS && retval == EEXIST) 294 return (IPADM_ADDRCONF_EXISTS); 295 return (ipadm_errno2status(retval)); 296 fail: 297 (void) close(fd); 298 return (IPADM_NDPD_NOT_RUNNING); 299 } 300 301 /* 302 * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed 303 * to by `buf'. 304 */ 305 int 306 ipadm_ndpd_read(int fd, void *buffer, size_t buflen) 307 { 308 int retval; 309 ssize_t nbytes = 0; /* total bytes processed */ 310 ssize_t prbytes; /* per-round bytes processed */ 311 struct pollfd pfd; 312 313 while (nbytes < buflen) { 314 315 pfd.fd = fd; 316 pfd.events = POLLIN; 317 318 /* 319 * Wait for data to come in or for the timeout to fire. 320 */ 321 retval = poll(&pfd, 1, NDPDTIMEOUT); 322 if (retval <= 0) { 323 if (retval == 0) 324 errno = ETIME; 325 break; 326 } 327 328 /* 329 * Descriptor is ready; have at it. 330 */ 331 prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes); 332 if (prbytes <= 0) { 333 if (prbytes == -1 && errno == EINTR) 334 continue; 335 break; 336 } 337 nbytes += prbytes; 338 } 339 340 return (nbytes == buflen ? 0 : -1); 341 } 342 343 /* 344 * Write `buflen' bytes from `buffer' to open file `fd'. Returns 0 345 * if all requested bytes were written, or an error code if not. 346 */ 347 int 348 ipadm_ndpd_write(int fd, const void *buffer, size_t buflen) 349 { 350 size_t nwritten; 351 ssize_t nbytes; 352 const char *buf = buffer; 353 354 for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { 355 nbytes = write(fd, &buf[nwritten], buflen - nwritten); 356 if (nbytes == -1) 357 return (-1); 358 if (nbytes == 0) { 359 errno = EIO; 360 return (-1); 361 } 362 } 363 364 assert(nwritten == buflen); 365 return (0); 366 } 367