xref: /illumos-gate/usr/src/lib/libipadm/common/ipadm_ndpd.c (revision c8152f8f417d34bc129af68ca0b76cfa9ca764db)
16e91bba0SGirish Moodalbail /*
26e91bba0SGirish Moodalbail  * CDDL HEADER START
36e91bba0SGirish Moodalbail  *
46e91bba0SGirish Moodalbail  * The contents of this file are subject to the terms of the
56e91bba0SGirish Moodalbail  * Common Development and Distribution License (the "License").
66e91bba0SGirish Moodalbail  * You may not use this file except in compliance with the License.
76e91bba0SGirish Moodalbail  *
86e91bba0SGirish Moodalbail  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96e91bba0SGirish Moodalbail  * or http://www.opensolaris.org/os/licensing.
106e91bba0SGirish Moodalbail  * See the License for the specific language governing permissions
116e91bba0SGirish Moodalbail  * and limitations under the License.
126e91bba0SGirish Moodalbail  *
136e91bba0SGirish Moodalbail  * When distributing Covered Code, include this CDDL HEADER in each
146e91bba0SGirish Moodalbail  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156e91bba0SGirish Moodalbail  * If applicable, add the following below this CDDL HEADER, with the
166e91bba0SGirish Moodalbail  * fields enclosed by brackets "[]" replaced with your own identifying
176e91bba0SGirish Moodalbail  * information: Portions Copyright [yyyy] [name of copyright owner]
186e91bba0SGirish Moodalbail  *
196e91bba0SGirish Moodalbail  * CDDL HEADER END
206e91bba0SGirish Moodalbail  */
216e91bba0SGirish Moodalbail /*
22ec3706caSVasumathi Sundaram  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23b31320a7SChris Fraire  * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
24*c8152f8fSAndy Fiddaman  * Copyright 2023 Oxide Computer Company
256e91bba0SGirish Moodalbail  */
266e91bba0SGirish Moodalbail 
276e91bba0SGirish Moodalbail /*
286e91bba0SGirish Moodalbail  * This file contains the functions that are required for communicating
296e91bba0SGirish Moodalbail  * with in.ndpd while creating autoconfigured addresses.
306e91bba0SGirish Moodalbail  */
316e91bba0SGirish Moodalbail 
326e91bba0SGirish Moodalbail #include <stdio.h>
336e91bba0SGirish Moodalbail #include <stdlib.h>
346e91bba0SGirish Moodalbail #include <string.h>
356e91bba0SGirish Moodalbail #include <strings.h>
366e91bba0SGirish Moodalbail #include <errno.h>
376e91bba0SGirish Moodalbail #include <fcntl.h>
386e91bba0SGirish Moodalbail #include <unistd.h>
396e91bba0SGirish Moodalbail #include <sys/sockio.h>
406e91bba0SGirish Moodalbail #include <sys/types.h>
416e91bba0SGirish Moodalbail #include <sys/stat.h>
426e91bba0SGirish Moodalbail #include <sys/socket.h>
436e91bba0SGirish Moodalbail #include <netinet/in.h>
446e91bba0SGirish Moodalbail #include <inet/ip.h>
456e91bba0SGirish Moodalbail #include <arpa/inet.h>
466e91bba0SGirish Moodalbail #include <assert.h>
476e91bba0SGirish Moodalbail #include <poll.h>
486e91bba0SGirish Moodalbail #include <ipadm_ndpd.h>
496e91bba0SGirish Moodalbail #include "libipadm_impl.h"
506e91bba0SGirish Moodalbail 
516e91bba0SGirish Moodalbail #define	NDPDTIMEOUT		5000
526e91bba0SGirish Moodalbail #define	PREFIXLEN_LINKLOCAL	10
536e91bba0SGirish Moodalbail 
546e91bba0SGirish Moodalbail static ipadm_status_t	i_ipadm_create_linklocal(ipadm_handle_t,
556e91bba0SGirish Moodalbail 			    ipadm_addrobj_t);
566e91bba0SGirish Moodalbail static void		i_ipadm_make_linklocal(struct sockaddr_in6 *,
576e91bba0SGirish Moodalbail 			    const struct in6_addr *);
586e91bba0SGirish Moodalbail static ipadm_status_t	i_ipadm_send_ndpd_cmd(const char *,
596e91bba0SGirish Moodalbail 			    const struct ipadm_addrobj_s *, int);
606e91bba0SGirish Moodalbail 
616e91bba0SGirish Moodalbail /*
626e91bba0SGirish Moodalbail  * Sends message to in.ndpd asking not to do autoconf for the given interface,
636e91bba0SGirish Moodalbail  * until IPADM_CREATE_ADDRS or IPADM_ENABLE_AUTOCONF is sent.
646e91bba0SGirish Moodalbail  */
656e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_disable_autoconf(const char * ifname)666e91bba0SGirish Moodalbail i_ipadm_disable_autoconf(const char *ifname)
676e91bba0SGirish Moodalbail {
686e91bba0SGirish Moodalbail 	return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_DISABLE_AUTOCONF));
696e91bba0SGirish Moodalbail }
706e91bba0SGirish Moodalbail 
716e91bba0SGirish Moodalbail /*
726e91bba0SGirish Moodalbail  * Sends message to in.ndpd to enable autoconf for the given interface,
736e91bba0SGirish Moodalbail  * until another IPADM_DISABLE_AUTOCONF is sent.
746e91bba0SGirish Moodalbail  */
756e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_enable_autoconf(const char * ifname)766e91bba0SGirish Moodalbail i_ipadm_enable_autoconf(const char *ifname)
776e91bba0SGirish Moodalbail {
786e91bba0SGirish Moodalbail 	return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_ENABLE_AUTOCONF));
796e91bba0SGirish Moodalbail }
806e91bba0SGirish Moodalbail 
816e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_create_ipv6addrs(ipadm_handle_t iph,ipadm_addrobj_t addr,uint32_t i_flags)826e91bba0SGirish Moodalbail i_ipadm_create_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t addr,
836e91bba0SGirish Moodalbail     uint32_t i_flags)
846e91bba0SGirish Moodalbail {
856e91bba0SGirish Moodalbail 	ipadm_status_t status;
866e91bba0SGirish Moodalbail 
876e91bba0SGirish Moodalbail 	/*
886e91bba0SGirish Moodalbail 	 * Create the link local based on the given token. If the same intfid
896e91bba0SGirish Moodalbail 	 * was already used with a different address object, this step will
906e91bba0SGirish Moodalbail 	 * fail.
916e91bba0SGirish Moodalbail 	 */
926e91bba0SGirish Moodalbail 	status = i_ipadm_create_linklocal(iph, addr);
936e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS)
946e91bba0SGirish Moodalbail 		return (status);
956e91bba0SGirish Moodalbail 
966e91bba0SGirish Moodalbail 	/*
976e91bba0SGirish Moodalbail 	 * Request in.ndpd to start the autoconfiguration.
986e91bba0SGirish Moodalbail 	 * If autoconfiguration was already started by another means (e.g.
996e91bba0SGirish Moodalbail 	 * "ifconfig" ), in.ndpd will return EEXIST.
1006e91bba0SGirish Moodalbail 	 */
1016e91bba0SGirish Moodalbail 	if (addr->ipadm_stateless || addr->ipadm_stateful) {
1026e91bba0SGirish Moodalbail 		status = i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr,
1036e91bba0SGirish Moodalbail 		    IPADM_CREATE_ADDRS);
1046e91bba0SGirish Moodalbail 		if (status != IPADM_SUCCESS &&
1056e91bba0SGirish Moodalbail 		    status != IPADM_NDPD_NOT_RUNNING) {
1066e91bba0SGirish Moodalbail 			(void) i_ipadm_delete_addr(iph, addr);
1076e91bba0SGirish Moodalbail 			return (status);
1086e91bba0SGirish Moodalbail 		}
1096e91bba0SGirish Moodalbail 	}
1106e91bba0SGirish Moodalbail 
1116e91bba0SGirish Moodalbail 	/* Persist the intfid. */
112b31320a7SChris Fraire 	status = i_ipadm_addr_persist(iph, addr, B_FALSE, i_flags, NULL);
1136e91bba0SGirish Moodalbail 	if (status != IPADM_SUCCESS) {
1146e91bba0SGirish Moodalbail 		(void) i_ipadm_delete_addr(iph, addr);
1156e91bba0SGirish Moodalbail 		(void) i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr,
1166e91bba0SGirish Moodalbail 		    IPADM_DELETE_ADDRS);
1176e91bba0SGirish Moodalbail 	}
1186e91bba0SGirish Moodalbail 
1196e91bba0SGirish Moodalbail 	return (status);
1206e91bba0SGirish Moodalbail }
1216e91bba0SGirish Moodalbail 
1226e91bba0SGirish Moodalbail ipadm_status_t
i_ipadm_delete_ipv6addrs(ipadm_handle_t iph,ipadm_addrobj_t ipaddr)1236e91bba0SGirish Moodalbail i_ipadm_delete_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
1246e91bba0SGirish Moodalbail {
1256e91bba0SGirish Moodalbail 	ipadm_status_t status;
1266e91bba0SGirish Moodalbail 
1276e91bba0SGirish Moodalbail 	/*
1286e91bba0SGirish Moodalbail 	 * Send a msg to in.ndpd to remove the autoconfigured addresses,
1296e91bba0SGirish Moodalbail 	 * and delete the link local that was created.
1306e91bba0SGirish Moodalbail 	 */
1316e91bba0SGirish Moodalbail 	status = i_ipadm_send_ndpd_cmd(ipaddr->ipadm_ifname, ipaddr,
1326e91bba0SGirish Moodalbail 	    IPADM_DELETE_ADDRS);
1332514b110SRyan Goodfellow 
1342514b110SRyan Goodfellow 	/* if the entry is not found, or ndpd is not running, just carry on */
1352514b110SRyan Goodfellow 	if (status == IPADM_NDPD_NOT_RUNNING || status == IPADM_ENXIO ||
1362514b110SRyan Goodfellow 	    status == IPADM_NOTFOUND)
1376e91bba0SGirish Moodalbail 		status = IPADM_SUCCESS;
1382514b110SRyan Goodfellow 
1396e91bba0SGirish Moodalbail 	if (status == IPADM_SUCCESS)
1406e91bba0SGirish Moodalbail 		status = i_ipadm_delete_addr(iph, ipaddr);
1416e91bba0SGirish Moodalbail 
1426e91bba0SGirish Moodalbail 	return (status);
1436e91bba0SGirish Moodalbail }
1446e91bba0SGirish Moodalbail 
1456e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_create_linklocal(ipadm_handle_t iph,ipadm_addrobj_t addr)1466e91bba0SGirish Moodalbail i_ipadm_create_linklocal(ipadm_handle_t iph, ipadm_addrobj_t addr)
1476e91bba0SGirish Moodalbail {
1486e91bba0SGirish Moodalbail 	boolean_t addif = B_FALSE;
1496e91bba0SGirish Moodalbail 	struct sockaddr_in6 *sin6;
1506e91bba0SGirish Moodalbail 	struct lifreq lifr;
1516e91bba0SGirish Moodalbail 	int err;
1526e91bba0SGirish Moodalbail 	ipadm_status_t status;
1536e91bba0SGirish Moodalbail 	in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1546e91bba0SGirish Moodalbail 	    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
1556e91bba0SGirish Moodalbail 
156ec3706caSVasumathi Sundaram 	/*
157ec3706caSVasumathi Sundaram 	 * Create a logical interface if needed.
158ec3706caSVasumathi Sundaram 	 */
159ec3706caSVasumathi Sundaram retry:
160*c8152f8fSAndy Fiddaman 	status = i_ipadm_do_addif(iph, addr, &addif);
161ec3706caSVasumathi Sundaram 	if (status != IPADM_SUCCESS)
162ec3706caSVasumathi Sundaram 		return (status);
163ec3706caSVasumathi Sundaram 	if (!(iph->iph_flags & IPH_INIT)) {
164ec3706caSVasumathi Sundaram 		status = i_ipadm_setlifnum_addrobj(iph, addr);
165ec3706caSVasumathi Sundaram 		if (status == IPADM_ADDROBJ_EXISTS)
166ec3706caSVasumathi Sundaram 			goto retry;
167ec3706caSVasumathi Sundaram 		if (status != IPADM_SUCCESS)
168ec3706caSVasumathi Sundaram 			return (status);
169ec3706caSVasumathi Sundaram 	}
170ec3706caSVasumathi Sundaram 
1716e91bba0SGirish Moodalbail 	bzero(&lifr, sizeof (lifr));
1726e91bba0SGirish Moodalbail 	(void) strlcpy(lifr.lifr_name, addr->ipadm_ifname, LIFNAMSIZ);
173ec3706caSVasumathi Sundaram 	i_ipadm_addrobj2lifname(addr, lifr.lifr_name, sizeof (lifr.lifr_name));
1746e91bba0SGirish Moodalbail 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
175ec3706caSVasumathi Sundaram 
1766e91bba0SGirish Moodalbail 	/* Create the link-local address */
1776e91bba0SGirish Moodalbail 	bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
17864639aafSDarren Reed 	(void) plen2mask(PREFIXLEN_LINKLOCAL, AF_INET6,
17964639aafSDarren Reed 	    (struct sockaddr *)&lifr.lifr_addr);
1806e91bba0SGirish Moodalbail 	if ((err = ioctl(iph->iph_sock6, SIOCSLIFNETMASK, (caddr_t)&lifr)) < 0)
1816e91bba0SGirish Moodalbail 		goto fail;
1826e91bba0SGirish Moodalbail 	if (addr->ipadm_intfidlen == 0) {
1836e91bba0SGirish Moodalbail 		/*
1846e91bba0SGirish Moodalbail 		 * If we have to use the default interface id,
1856e91bba0SGirish Moodalbail 		 * we just need to set the prefix to the link-local prefix.
1866e91bba0SGirish Moodalbail 		 * SIOCSLIFPREFIX sets the address with the given prefix
1876e91bba0SGirish Moodalbail 		 * and the default interface id.
1886e91bba0SGirish Moodalbail 		 */
1896e91bba0SGirish Moodalbail 		sin6->sin6_addr = ll_template;
1906e91bba0SGirish Moodalbail 		err = ioctl(iph->iph_sock6, SIOCSLIFPREFIX, (caddr_t)&lifr);
1916e91bba0SGirish Moodalbail 		if (err < 0)
1926e91bba0SGirish Moodalbail 			goto fail;
1936e91bba0SGirish Moodalbail 	} else {
1946e91bba0SGirish Moodalbail 		/* Make a linklocal address in sin6 and set it */
1956e91bba0SGirish Moodalbail 		i_ipadm_make_linklocal(sin6, &addr->ipadm_intfid.sin6_addr);
1966e91bba0SGirish Moodalbail 		err = ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr);
1976e91bba0SGirish Moodalbail 		if (err < 0)
1986e91bba0SGirish Moodalbail 			goto fail;
1996e91bba0SGirish Moodalbail 	}
2006e91bba0SGirish Moodalbail 	if ((err = ioctl(iph->iph_sock6, SIOCGLIFFLAGS, (char *)&lifr)) < 0)
2016e91bba0SGirish Moodalbail 		goto fail;
2026e91bba0SGirish Moodalbail 	lifr.lifr_flags |= IFF_UP;
2036e91bba0SGirish Moodalbail 	if ((err = ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (char *)&lifr)) < 0)
2046e91bba0SGirish Moodalbail 		goto fail;
2056e91bba0SGirish Moodalbail 	return (IPADM_SUCCESS);
2066e91bba0SGirish Moodalbail 
2076e91bba0SGirish Moodalbail fail:
2086e91bba0SGirish Moodalbail 	if (errno == EEXIST)
2096e91bba0SGirish Moodalbail 		status = IPADM_ADDRCONF_EXISTS;
2106e91bba0SGirish Moodalbail 	else
2116e91bba0SGirish Moodalbail 		status = ipadm_errno2status(errno);
2126e91bba0SGirish Moodalbail 	/* Remove the linklocal that was created. */
2136e91bba0SGirish Moodalbail 	if (addif) {
2146e91bba0SGirish Moodalbail 		(void) ioctl(iph->iph_sock6, SIOCLIFREMOVEIF, (caddr_t)&lifr);
2156e91bba0SGirish Moodalbail 	} else {
2166e91bba0SGirish Moodalbail 		struct sockaddr_in6 *sin6;
2176e91bba0SGirish Moodalbail 
2186e91bba0SGirish Moodalbail 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
2196e91bba0SGirish Moodalbail 		lifr.lifr_flags &= ~IFF_UP;
2206e91bba0SGirish Moodalbail 		(void) ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (caddr_t)&lifr);
2216e91bba0SGirish Moodalbail 		sin6->sin6_family = AF_INET6;
2226e91bba0SGirish Moodalbail 		sin6->sin6_addr = in6addr_any;
2236e91bba0SGirish Moodalbail 		(void) ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr);
2246e91bba0SGirish Moodalbail 	}
2256e91bba0SGirish Moodalbail 	return (status);
2266e91bba0SGirish Moodalbail }
2276e91bba0SGirish Moodalbail 
2286e91bba0SGirish Moodalbail /*
2296e91bba0SGirish Moodalbail  * Make a linklocal address based on the given intfid and copy it into
2306e91bba0SGirish Moodalbail  * the output parameter `sin6'.
2316e91bba0SGirish Moodalbail  */
2326e91bba0SGirish Moodalbail static void
i_ipadm_make_linklocal(struct sockaddr_in6 * sin6,const struct in6_addr * intfid)2336e91bba0SGirish Moodalbail i_ipadm_make_linklocal(struct sockaddr_in6 *sin6, const struct in6_addr *intfid)
2346e91bba0SGirish Moodalbail {
2356e91bba0SGirish Moodalbail 	int i;
2366e91bba0SGirish Moodalbail 	in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
2376e91bba0SGirish Moodalbail 	    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
2386e91bba0SGirish Moodalbail 
2396e91bba0SGirish Moodalbail 	sin6->sin6_family = AF_INET6;
2406e91bba0SGirish Moodalbail 	sin6->sin6_addr = *intfid;
2416e91bba0SGirish Moodalbail 	for (i = 0; i < 4; i++) {
2426e91bba0SGirish Moodalbail 		sin6->sin6_addr.s6_addr[i] =
2436e91bba0SGirish Moodalbail 		    sin6->sin6_addr.s6_addr[i] | ll_template.s6_addr[i];
2446e91bba0SGirish Moodalbail 	}
2456e91bba0SGirish Moodalbail }
2466e91bba0SGirish Moodalbail 
2476e91bba0SGirish Moodalbail /*
2486e91bba0SGirish Moodalbail  * Function that forms an ndpd msg and sends it to the in.ndpd daemon's loopback
2496e91bba0SGirish Moodalbail  * listener socket.
2506e91bba0SGirish Moodalbail  */
2516e91bba0SGirish Moodalbail static ipadm_status_t
i_ipadm_send_ndpd_cmd(const char * ifname,const struct ipadm_addrobj_s * addr,int cmd)2526e91bba0SGirish Moodalbail i_ipadm_send_ndpd_cmd(const char *ifname, const struct ipadm_addrobj_s *addr,
2536e91bba0SGirish Moodalbail     int cmd)
2546e91bba0SGirish Moodalbail {
2556e91bba0SGirish Moodalbail 	int fd;
2566e91bba0SGirish Moodalbail 	struct sockaddr_un servaddr;
2576e91bba0SGirish Moodalbail 	int flags;
2586e91bba0SGirish Moodalbail 	ipadm_ndpd_msg_t msg;
2596e91bba0SGirish Moodalbail 	int retval;
2606e91bba0SGirish Moodalbail 
2616e91bba0SGirish Moodalbail 	if (addr == NULL &&
2626e91bba0SGirish Moodalbail 	    (cmd == IPADM_CREATE_ADDRS || cmd == IPADM_DELETE_ADDRS)) {
2636e91bba0SGirish Moodalbail 		return (IPADM_INVALID_ARG);
2646e91bba0SGirish Moodalbail 	}
2656e91bba0SGirish Moodalbail 
2666e91bba0SGirish Moodalbail 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
2676e91bba0SGirish Moodalbail 	if (fd == -1)
2686e91bba0SGirish Moodalbail 		return (IPADM_FAILURE);
2696e91bba0SGirish Moodalbail 
2706e91bba0SGirish Moodalbail 	/* Put the socket in non-blocking mode */
2716e91bba0SGirish Moodalbail 	flags = fcntl(fd, F_GETFL, 0);
2726e91bba0SGirish Moodalbail 	if (flags != -1)
2736e91bba0SGirish Moodalbail 		(void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
2746e91bba0SGirish Moodalbail 
2756e91bba0SGirish Moodalbail 	/* Connect to in.ndpd */
2766e91bba0SGirish Moodalbail 	bzero(&servaddr, sizeof (servaddr));
2776e91bba0SGirish Moodalbail 	servaddr.sun_family = AF_UNIX;
2786e91bba0SGirish Moodalbail 	(void) strlcpy(servaddr.sun_path, IPADM_UDS_PATH,
2796e91bba0SGirish Moodalbail 	    sizeof (servaddr.sun_path));
2806e91bba0SGirish Moodalbail 	if (connect(fd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1)
2816e91bba0SGirish Moodalbail 		goto fail;
2826e91bba0SGirish Moodalbail 
2836e91bba0SGirish Moodalbail 	bzero(&msg, sizeof (msg));
2846e91bba0SGirish Moodalbail 	msg.inm_cmd = cmd;
2856e91bba0SGirish Moodalbail 	(void) strlcpy(msg.inm_ifname, ifname, sizeof (msg.inm_ifname));
2866e91bba0SGirish Moodalbail 	if (addr != NULL) {
2876e91bba0SGirish Moodalbail 		msg.inm_intfid = addr->ipadm_intfid;
2886e91bba0SGirish Moodalbail 		msg.inm_intfidlen = addr->ipadm_intfidlen;
2896e91bba0SGirish Moodalbail 		msg.inm_stateless = addr->ipadm_stateless;
2906e91bba0SGirish Moodalbail 		msg.inm_stateful = addr->ipadm_stateful;
2916e91bba0SGirish Moodalbail 		if (cmd == IPADM_CREATE_ADDRS) {
2926e91bba0SGirish Moodalbail 			(void) strlcpy(msg.inm_aobjname, addr->ipadm_aobjname,
2936e91bba0SGirish Moodalbail 			    sizeof (msg.inm_aobjname));
2946e91bba0SGirish Moodalbail 		}
2956e91bba0SGirish Moodalbail 	}
2966e91bba0SGirish Moodalbail 	if (ipadm_ndpd_write(fd, &msg, sizeof (msg)) < 0)
2976e91bba0SGirish Moodalbail 		goto fail;
2986e91bba0SGirish Moodalbail 	if (ipadm_ndpd_read(fd, &retval, sizeof (retval)) < 0)
2996e91bba0SGirish Moodalbail 		goto fail;
3006e91bba0SGirish Moodalbail 	(void) close(fd);
3016e91bba0SGirish Moodalbail 	if (cmd == IPADM_CREATE_ADDRS && retval == EEXIST)
3026e91bba0SGirish Moodalbail 		return (IPADM_ADDRCONF_EXISTS);
3036e91bba0SGirish Moodalbail 	return (ipadm_errno2status(retval));
3046e91bba0SGirish Moodalbail fail:
3056e91bba0SGirish Moodalbail 	(void) close(fd);
3066e91bba0SGirish Moodalbail 	return (IPADM_NDPD_NOT_RUNNING);
3076e91bba0SGirish Moodalbail }
3086e91bba0SGirish Moodalbail 
3096e91bba0SGirish Moodalbail /*
3106e91bba0SGirish Moodalbail  * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
3116e91bba0SGirish Moodalbail  * to by `buf'.
3126e91bba0SGirish Moodalbail  */
3136e91bba0SGirish Moodalbail int
ipadm_ndpd_read(int fd,void * buffer,size_t buflen)3146e91bba0SGirish Moodalbail ipadm_ndpd_read(int fd, void *buffer, size_t buflen)
3156e91bba0SGirish Moodalbail {
3166e91bba0SGirish Moodalbail 	int		retval;
3176e91bba0SGirish Moodalbail 	ssize_t		nbytes = 0;	/* total bytes processed */
3186e91bba0SGirish Moodalbail 	ssize_t		prbytes;	/* per-round bytes processed */
3196e91bba0SGirish Moodalbail 	struct pollfd	pfd;
3206e91bba0SGirish Moodalbail 
3216e91bba0SGirish Moodalbail 	while (nbytes < buflen) {
3226e91bba0SGirish Moodalbail 
3236e91bba0SGirish Moodalbail 		pfd.fd = fd;
3246e91bba0SGirish Moodalbail 		pfd.events = POLLIN;
3256e91bba0SGirish Moodalbail 
3266e91bba0SGirish Moodalbail 		/*
3276e91bba0SGirish Moodalbail 		 * Wait for data to come in or for the timeout to fire.
3286e91bba0SGirish Moodalbail 		 */
3296e91bba0SGirish Moodalbail 		retval = poll(&pfd, 1, NDPDTIMEOUT);
3306e91bba0SGirish Moodalbail 		if (retval <= 0) {
3316e91bba0SGirish Moodalbail 			if (retval == 0)
3326e91bba0SGirish Moodalbail 				errno = ETIME;
3336e91bba0SGirish Moodalbail 			break;
3346e91bba0SGirish Moodalbail 		}
3356e91bba0SGirish Moodalbail 
3366e91bba0SGirish Moodalbail 		/*
3376e91bba0SGirish Moodalbail 		 * Descriptor is ready; have at it.
3386e91bba0SGirish Moodalbail 		 */
3396e91bba0SGirish Moodalbail 		prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
3406e91bba0SGirish Moodalbail 		if (prbytes <= 0) {
3416e91bba0SGirish Moodalbail 			if (prbytes == -1 && errno == EINTR)
3426e91bba0SGirish Moodalbail 				continue;
3436e91bba0SGirish Moodalbail 			break;
3446e91bba0SGirish Moodalbail 		}
3456e91bba0SGirish Moodalbail 		nbytes += prbytes;
3466e91bba0SGirish Moodalbail 	}
3476e91bba0SGirish Moodalbail 
3486e91bba0SGirish Moodalbail 	return (nbytes == buflen ? 0 : -1);
3496e91bba0SGirish Moodalbail }
3506e91bba0SGirish Moodalbail 
3516e91bba0SGirish Moodalbail /*
3526e91bba0SGirish Moodalbail  * Write `buflen' bytes from `buffer' to open file `fd'.  Returns 0
3536e91bba0SGirish Moodalbail  * if all requested bytes were written, or an error code if not.
3546e91bba0SGirish Moodalbail  */
3556e91bba0SGirish Moodalbail int
ipadm_ndpd_write(int fd,const void * buffer,size_t buflen)3566e91bba0SGirish Moodalbail ipadm_ndpd_write(int fd, const void *buffer, size_t buflen)
3576e91bba0SGirish Moodalbail {
3586e91bba0SGirish Moodalbail 	size_t		nwritten;
3596e91bba0SGirish Moodalbail 	ssize_t		nbytes;
3606e91bba0SGirish Moodalbail 	const char	*buf = buffer;
3616e91bba0SGirish Moodalbail 
3626e91bba0SGirish Moodalbail 	for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
3636e91bba0SGirish Moodalbail 		nbytes = write(fd, &buf[nwritten], buflen - nwritten);
3646e91bba0SGirish Moodalbail 		if (nbytes == -1)
3656e91bba0SGirish Moodalbail 			return (-1);
3666e91bba0SGirish Moodalbail 		if (nbytes == 0) {
3676e91bba0SGirish Moodalbail 			errno = EIO;
3686e91bba0SGirish Moodalbail 			return (-1);
3696e91bba0SGirish Moodalbail 		}
3706e91bba0SGirish Moodalbail 	}
3716e91bba0SGirish Moodalbail 
3726e91bba0SGirish Moodalbail 	assert(nwritten == buflen);
3736e91bba0SGirish Moodalbail 	return (0);
3746e91bba0SGirish Moodalbail }
375