Merge commit 'b31320a79e2054c6739b5229259dbf98f3afc547' into merges
[unleashed.git] / usr / src / lib / libipadm / common / ipadm_ndpd.c
blob602e3cc15e066168c699658c363284c57bfc2327
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
27 * This file contains the functions that are required for communicating
28 * with in.ndpd while creating autoconfigured addresses.
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"
50 #define NDPDTIMEOUT 5000
51 #define PREFIXLEN_LINKLOCAL 10
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);
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.
64 ipadm_status_t
65 i_ipadm_disable_autoconf(const char *ifname)
67 return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_DISABLE_AUTOCONF));
71 * Sends message to in.ndpd to enable autoconf for the given interface,
72 * until another IPADM_DISABLE_AUTOCONF is sent.
74 ipadm_status_t
75 i_ipadm_enable_autoconf(const char *ifname)
77 return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_ENABLE_AUTOCONF));
80 ipadm_status_t
81 i_ipadm_create_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t addr,
82 uint32_t i_flags)
84 ipadm_status_t status;
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.
91 status = i_ipadm_create_linklocal(iph, addr);
92 if (status != IPADM_SUCCESS)
93 return (status);
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.
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);
110 /* Persist the intfid. */
111 status = i_ipadm_addr_persist(iph, addr, B_FALSE, i_flags, NULL);
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);
118 return (status);
121 ipadm_status_t
122 i_ipadm_delete_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
124 ipadm_status_t status;
127 * Send a msg to in.ndpd to remove the autoconfigured addresses,
128 * and delete the link local that was created.
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);
137 return (status);
140 static ipadm_status_t
141 i_ipadm_create_linklocal(ipadm_handle_t iph, ipadm_addrobj_t addr)
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 } } };
152 * Create a logical interface if needed.
154 retry:
155 status = i_ipadm_do_addif(iph, addr);
156 if (status != IPADM_SUCCESS)
157 return (status);
158 if (!(iph->iph_flags & IPH_INIT)) {
159 status = i_ipadm_setlifnum_addrobj(iph, addr);
160 if (status == IPADM_ADDROBJ_EXISTS)
161 goto retry;
162 if (status != IPADM_SUCCESS)
163 return (status);
166 bzero(&lifr, sizeof (lifr));
167 (void) strlcpy(lifr.lifr_name, addr->ipadm_ifname, LIFNAMSIZ);
168 i_ipadm_addrobj2lifname(addr, lifr.lifr_name, sizeof (lifr.lifr_name));
169 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
171 /* Create the link-local address */
172 bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
173 (void) plen2mask(PREFIXLEN_LINKLOCAL, AF_INET6,
174 (struct sockaddr *)&lifr.lifr_addr);
175 if ((err = ioctl(iph->iph_sock6, SIOCSLIFNETMASK, (caddr_t)&lifr)) < 0)
176 goto fail;
177 if (addr->ipadm_intfidlen == 0) {
179 * If we have to use the default interface id,
180 * we just need to set the prefix to the link-local prefix.
181 * SIOCSLIFPREFIX sets the address with the given prefix
182 * and the default interface id.
184 sin6->sin6_addr = ll_template;
185 err = ioctl(iph->iph_sock6, SIOCSLIFPREFIX, (caddr_t)&lifr);
186 if (err < 0)
187 goto fail;
188 } else {
189 /* Make a linklocal address in sin6 and set it */
190 i_ipadm_make_linklocal(sin6, &addr->ipadm_intfid.sin6_addr);
191 err = ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr);
192 if (err < 0)
193 goto fail;
195 if ((err = ioctl(iph->iph_sock6, SIOCGLIFFLAGS, (char *)&lifr)) < 0)
196 goto fail;
197 lifr.lifr_flags |= IFF_UP;
198 if ((err = ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (char *)&lifr)) < 0)
199 goto fail;
200 return (IPADM_SUCCESS);
202 fail:
203 if (errno == EEXIST)
204 status = IPADM_ADDRCONF_EXISTS;
205 else
206 status = ipadm_errno2status(errno);
207 /* Remove the linklocal that was created. */
208 if (addif) {
209 (void) ioctl(iph->iph_sock6, SIOCLIFREMOVEIF, (caddr_t)&lifr);
210 } else {
211 struct sockaddr_in6 *sin6;
213 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
214 lifr.lifr_flags &= ~IFF_UP;
215 (void) ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (caddr_t)&lifr);
216 sin6->sin6_family = AF_INET6;
217 sin6->sin6_addr = in6addr_any;
218 (void) ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr);
220 return (status);
224 * Make a linklocal address based on the given intfid and copy it into
225 * the output parameter `sin6'.
227 static void
228 i_ipadm_make_linklocal(struct sockaddr_in6 *sin6, const struct in6_addr *intfid)
230 int i;
231 in6_addr_t ll_template = { { { 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
232 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } } };
234 sin6->sin6_family = AF_INET6;
235 sin6->sin6_addr = *intfid;
236 for (i = 0; i < 4; i++) {
237 sin6->sin6_addr.s6_addr[i] =
238 sin6->sin6_addr.s6_addr[i] | ll_template.s6_addr[i];
243 * Function that forms an ndpd msg and sends it to the in.ndpd daemon's loopback
244 * listener socket.
246 static ipadm_status_t
247 i_ipadm_send_ndpd_cmd(const char *ifname, const struct ipadm_addrobj_s *addr,
248 int cmd)
250 int fd;
251 struct sockaddr_un servaddr;
252 int flags;
253 ipadm_ndpd_msg_t msg;
254 int retval;
256 if (addr == NULL &&
257 (cmd == IPADM_CREATE_ADDRS || cmd == IPADM_DELETE_ADDRS)) {
258 return (IPADM_INVALID_ARG);
261 fd = socket(AF_UNIX, SOCK_STREAM, 0);
262 if (fd == -1)
263 return (IPADM_FAILURE);
265 /* Put the socket in non-blocking mode */
266 flags = fcntl(fd, F_GETFL, 0);
267 if (flags != -1)
268 (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
270 /* Connect to in.ndpd */
271 bzero(&servaddr, sizeof (servaddr));
272 servaddr.sun_family = AF_UNIX;
273 (void) strlcpy(servaddr.sun_path, IPADM_UDS_PATH,
274 sizeof (servaddr.sun_path));
275 if (connect(fd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1)
276 goto fail;
278 bzero(&msg, sizeof (msg));
279 msg.inm_cmd = cmd;
280 (void) strlcpy(msg.inm_ifname, ifname, sizeof (msg.inm_ifname));
281 if (addr != NULL) {
282 msg.inm_intfid = addr->ipadm_intfid;
283 msg.inm_intfidlen = addr->ipadm_intfidlen;
284 msg.inm_stateless = addr->ipadm_stateless;
285 msg.inm_stateful = addr->ipadm_stateful;
286 if (cmd == IPADM_CREATE_ADDRS) {
287 (void) strlcpy(msg.inm_aobjname, addr->ipadm_aobjname,
288 sizeof (msg.inm_aobjname));
291 if (ipadm_ndpd_write(fd, &msg, sizeof (msg)) < 0)
292 goto fail;
293 if (ipadm_ndpd_read(fd, &retval, sizeof (retval)) < 0)
294 goto fail;
295 (void) close(fd);
296 if (cmd == IPADM_CREATE_ADDRS && retval == EEXIST)
297 return (IPADM_ADDRCONF_EXISTS);
298 return (ipadm_errno2status(retval));
299 fail:
300 (void) close(fd);
301 return (IPADM_NDPD_NOT_RUNNING);
305 * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
306 * to by `buf'.
309 ipadm_ndpd_read(int fd, void *buffer, size_t buflen)
311 int retval;
312 ssize_t nbytes = 0; /* total bytes processed */
313 ssize_t prbytes; /* per-round bytes processed */
314 struct pollfd pfd;
316 while (nbytes < buflen) {
318 pfd.fd = fd;
319 pfd.events = POLLIN;
322 * Wait for data to come in or for the timeout to fire.
324 retval = poll(&pfd, 1, NDPDTIMEOUT);
325 if (retval <= 0) {
326 if (retval == 0)
327 errno = ETIME;
328 break;
332 * Descriptor is ready; have at it.
334 prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
335 if (prbytes <= 0) {
336 if (prbytes == -1 && errno == EINTR)
337 continue;
338 break;
340 nbytes += prbytes;
343 return (nbytes == buflen ? 0 : -1);
347 * Write `buflen' bytes from `buffer' to open file `fd'. Returns 0
348 * if all requested bytes were written, or an error code if not.
351 ipadm_ndpd_write(int fd, const void *buffer, size_t buflen)
353 size_t nwritten;
354 ssize_t nbytes;
355 const char *buf = buffer;
357 for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
358 nbytes = write(fd, &buf[nwritten], buflen - nwritten);
359 if (nbytes == -1)
360 return (-1);
361 if (nbytes == 0) {
362 errno = EIO;
363 return (-1);
367 assert(nwritten == buflen);
368 return (0);