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]
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.
38 #include <sys/sockio.h>
39 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
44 #include <arpa/inet.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
,
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.
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.
75 i_ipadm_enable_autoconf(const char *ifname
)
77 return (i_ipadm_send_ndpd_cmd(ifname
, NULL
, IPADM_ENABLE_AUTOCONF
));
81 i_ipadm_create_ipv6addrs(ipadm_handle_t iph
, ipadm_addrobj_t addr
,
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
91 status
= i_ipadm_create_linklocal(iph
, addr
);
92 if (status
!= IPADM_SUCCESS
)
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
,
103 if (status
!= IPADM_SUCCESS
&&
104 status
!= IPADM_NDPD_NOT_RUNNING
) {
105 (void) i_ipadm_delete_addr(iph
, addr
);
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
,
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
,
132 if (status
== IPADM_NDPD_NOT_RUNNING
)
133 status
= IPADM_SUCCESS
;
134 if (status
== IPADM_SUCCESS
)
135 status
= i_ipadm_delete_addr(iph
, ipaddr
);
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
;
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.
155 status
= i_ipadm_do_addif(iph
, addr
);
156 if (status
!= IPADM_SUCCESS
)
158 if (!(iph
->iph_flags
& IPH_INIT
)) {
159 status
= i_ipadm_setlifnum_addrobj(iph
, addr
);
160 if (status
== IPADM_ADDROBJ_EXISTS
)
162 if (status
!= IPADM_SUCCESS
)
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)
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
);
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
);
195 if ((err
= ioctl(iph
->iph_sock6
, SIOCGLIFFLAGS
, (char *)&lifr
)) < 0)
197 lifr
.lifr_flags
|= IFF_UP
;
198 if ((err
= ioctl(iph
->iph_sock6
, SIOCSLIFFLAGS
, (char *)&lifr
)) < 0)
200 return (IPADM_SUCCESS
);
204 status
= IPADM_ADDRCONF_EXISTS
;
206 status
= ipadm_errno2status(errno
);
207 /* Remove the linklocal that was created. */
209 (void) ioctl(iph
->iph_sock6
, SIOCLIFREMOVEIF
, (caddr_t
)&lifr
);
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
);
224 * Make a linklocal address based on the given intfid and copy it into
225 * the output parameter `sin6'.
228 i_ipadm_make_linklocal(struct sockaddr_in6
*sin6
, const struct in6_addr
*intfid
)
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
246 static ipadm_status_t
247 i_ipadm_send_ndpd_cmd(const char *ifname
, const struct ipadm_addrobj_s
*addr
,
251 struct sockaddr_un servaddr
;
253 ipadm_ndpd_msg_t msg
;
257 (cmd
== IPADM_CREATE_ADDRS
|| cmd
== IPADM_DELETE_ADDRS
)) {
258 return (IPADM_INVALID_ARG
);
261 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
263 return (IPADM_FAILURE
);
265 /* Put the socket in non-blocking mode */
266 flags
= fcntl(fd
, F_GETFL
, 0);
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)
278 bzero(&msg
, sizeof (msg
));
280 (void) strlcpy(msg
.inm_ifname
, ifname
, sizeof (msg
.inm_ifname
));
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)
293 if (ipadm_ndpd_read(fd
, &retval
, sizeof (retval
)) < 0)
296 if (cmd
== IPADM_CREATE_ADDRS
&& retval
== EEXIST
)
297 return (IPADM_ADDRCONF_EXISTS
);
298 return (ipadm_errno2status(retval
));
301 return (IPADM_NDPD_NOT_RUNNING
);
305 * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
309 ipadm_ndpd_read(int fd
, void *buffer
, size_t buflen
)
312 ssize_t nbytes
= 0; /* total bytes processed */
313 ssize_t prbytes
; /* per-round bytes processed */
316 while (nbytes
< buflen
) {
322 * Wait for data to come in or for the timeout to fire.
324 retval
= poll(&pfd
, 1, NDPDTIMEOUT
);
332 * Descriptor is ready; have at it.
334 prbytes
= read(fd
, (caddr_t
)buffer
+ nbytes
, buflen
- nbytes
);
336 if (prbytes
== -1 && errno
== EINTR
)
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
)
355 const char *buf
= buffer
;
357 for (nwritten
= 0; nwritten
< buflen
; nwritten
+= nbytes
) {
358 nbytes
= write(fd
, &buf
[nwritten
], buflen
- nwritten
);
367 assert(nwritten
== buflen
);