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, 173 (struct sockaddr *)&lifr.lifr_addr); 174 if ((err = ioctl(iph->iph_sock6, SIOCSLIFNETMASK, (caddr_t)&lifr)) < 0) 175 goto fail; 176 if (addr->ipadm_intfidlen == 0) { 177 /* 178 * If we have to use the default interface id, 179 * we just need to set the prefix to the link-local prefix. 180 * SIOCSLIFPREFIX sets the address with the given prefix 181 * and the default interface id. 182 */ 183 sin6->sin6_addr = ll_template; 184 err = ioctl(iph->iph_sock6, SIOCSLIFPREFIX, (caddr_t)&lifr); 185 if (err < 0) 186 goto fail; 187 } else { 188 /* Make a linklocal address in sin6 and set it */ 189 i_ipadm_make_linklocal(sin6, &addr->ipadm_intfid.sin6_addr); 190 err = ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr); 191 if (err < 0) 192 goto fail; 193 } 194 if ((err = ioctl(iph->iph_sock6, SIOCGLIFFLAGS, (char *)&lifr)) < 0) 195 goto fail; 196 lifr.lifr_flags |= IFF_UP; 197 if ((err = ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (char *)&lifr)) < 0) 198 goto fail; 199 return (IPADM_SUCCESS); 200 201 fail: 202 if (errno == EEXIST) 203 status = IPADM_ADDRCONF_EXISTS; 204 else 205 status = ipadm_errno2status(errno); 206 /* Remove the linklocal that was created. */ 207 if (addif) { 208 (void) ioctl(iph->iph_sock6, SIOCLIFREMOVEIF, (caddr_t)&lifr); 209 } else { 210 struct sockaddr_in6 *sin6; 211 212 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; 213 lifr.lifr_flags &= ~IFF_UP; 214 (void) ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (caddr_t)&lifr); 215 sin6->sin6_family = AF_INET6; 216 sin6->sin6_addr = in6addr_any; 217 (void) ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr); 218 } 219 return (status); 220 } 221 222 /* 223 * Make a linklocal address based on the given intfid and copy it into 224 * the output parameter `sin6'. 225 */ 226 static void 227 i_ipadm_make_linklocal(struct sockaddr_in6 *sin6, const struct in6_addr *intfid) 228 { 229 int i; 230 in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 231 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; 232 233 sin6->sin6_family = AF_INET6; 234 sin6->sin6_addr = *intfid; 235 for (i = 0; i < 4; i++) { 236 sin6->sin6_addr.s6_addr[i] = 237 sin6->sin6_addr.s6_addr[i] | ll_template.s6_addr[i]; 238 } 239 } 240 241 /* 242 * Function that forms an ndpd msg and sends it to the in.ndpd daemon's loopback 243 * listener socket. 244 */ 245 static ipadm_status_t 246 i_ipadm_send_ndpd_cmd(const char *ifname, const struct ipadm_addrobj_s *addr, 247 int cmd) 248 { 249 int fd; 250 struct sockaddr_un servaddr; 251 int flags; 252 ipadm_ndpd_msg_t msg; 253 int retval; 254 255 if (addr == NULL && 256 (cmd == IPADM_CREATE_ADDRS || cmd == IPADM_DELETE_ADDRS)) { 257 return (IPADM_INVALID_ARG); 258 } 259 260 fd = socket(AF_UNIX, SOCK_STREAM, 0); 261 if (fd == -1) 262 return (IPADM_FAILURE); 263 264 /* Put the socket in non-blocking mode */ 265 flags = fcntl(fd, F_GETFL, 0); 266 if (flags != -1) 267 (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK); 268 269 /* Connect to in.ndpd */ 270 bzero(&servaddr, sizeof (servaddr)); 271 servaddr.sun_family = AF_UNIX; 272 (void) strlcpy(servaddr.sun_path, IPADM_UDS_PATH, 273 sizeof (servaddr.sun_path)); 274 if (connect(fd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1) 275 goto fail; 276 277 bzero(&msg, sizeof (msg)); 278 msg.inm_cmd = cmd; 279 (void) strlcpy(msg.inm_ifname, ifname, sizeof (msg.inm_ifname)); 280 if (addr != NULL) { 281 msg.inm_intfid = addr->ipadm_intfid; 282 msg.inm_intfidlen = addr->ipadm_intfidlen; 283 msg.inm_stateless = addr->ipadm_stateless; 284 msg.inm_stateful = addr->ipadm_stateful; 285 if (cmd == IPADM_CREATE_ADDRS) { 286 (void) strlcpy(msg.inm_aobjname, addr->ipadm_aobjname, 287 sizeof (msg.inm_aobjname)); 288 } 289 } 290 if (ipadm_ndpd_write(fd, &msg, sizeof (msg)) < 0) 291 goto fail; 292 if (ipadm_ndpd_read(fd, &retval, sizeof (retval)) < 0) 293 goto fail; 294 (void) close(fd); 295 if (cmd == IPADM_CREATE_ADDRS && retval == EEXIST) 296 return (IPADM_ADDRCONF_EXISTS); 297 return (ipadm_errno2status(retval)); 298 fail: 299 (void) close(fd); 300 return (IPADM_NDPD_NOT_RUNNING); 301 } 302 303 /* 304 * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed 305 * to by `buf'. 306 */ 307 int 308 ipadm_ndpd_read(int fd, void *buffer, size_t buflen) 309 { 310 int retval; 311 ssize_t nbytes = 0; /* total bytes processed */ 312 ssize_t prbytes; /* per-round bytes processed */ 313 struct pollfd pfd; 314 315 while (nbytes < buflen) { 316 317 pfd.fd = fd; 318 pfd.events = POLLIN; 319 320 /* 321 * Wait for data to come in or for the timeout to fire. 322 */ 323 retval = poll(&pfd, 1, NDPDTIMEOUT); 324 if (retval <= 0) { 325 if (retval == 0) 326 errno = ETIME; 327 break; 328 } 329 330 /* 331 * Descriptor is ready; have at it. 332 */ 333 prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes); 334 if (prbytes <= 0) { 335 if (prbytes == -1 && errno == EINTR) 336 continue; 337 break; 338 } 339 nbytes += prbytes; 340 } 341 342 return (nbytes == buflen ? 0 : -1); 343 } 344 345 /* 346 * Write `buflen' bytes from `buffer' to open file `fd'. Returns 0 347 * if all requested bytes were written, or an error code if not. 348 */ 349 int 350 ipadm_ndpd_write(int fd, const void *buffer, size_t buflen) 351 { 352 size_t nwritten; 353 ssize_t nbytes; 354 const char *buf = buffer; 355 356 for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { 357 nbytes = write(fd, &buf[nwritten], buflen - nwritten); 358 if (nbytes == -1) 359 return (-1); 360 if (nbytes == 0) { 361 errno = EIO; 362 return (-1); 363 } 364 } 365 366 assert(nwritten == buflen); 367 return (0); 368 } 369