xref: /illumos-gate/usr/src/lib/libipadm/common/ipadm_ndpd.c (revision 6e91bba0d6c6bdabbba62cefae583715a4a58e2a)
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