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