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