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