17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5c2934490Sdh155122 * Common Development and Distribution License (the "License").
6c2934490Sdh155122 * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22e11c3f44Smeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/socket.h>
287c478bd9Sstevel@tonic-gate #include <net/if.h>
297c478bd9Sstevel@tonic-gate #include <stdlib.h>
307c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
317c478bd9Sstevel@tonic-gate #include <netinet/in.h>
327c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
337c478bd9Sstevel@tonic-gate #include <string.h>
347c478bd9Sstevel@tonic-gate #include <unistd.h>
35d04ccbb3Scarlsonj #include <search.h>
367c478bd9Sstevel@tonic-gate #include <libdevinfo.h>
37948f2876Sss150715 #include <libdlpi.h>
38d04ccbb3Scarlsonj #include <netinet/if_ether.h>
39d04ccbb3Scarlsonj #include <arpa/inet.h>
40d04ccbb3Scarlsonj #include <dhcpmsg.h>
417c478bd9Sstevel@tonic-gate
42d04ccbb3Scarlsonj #include "agent.h"
437c478bd9Sstevel@tonic-gate #include "interface.h"
447c478bd9Sstevel@tonic-gate #include "util.h"
457c478bd9Sstevel@tonic-gate #include "packet.h"
467c478bd9Sstevel@tonic-gate #include "states.h"
47d04ccbb3Scarlsonj
48d04ccbb3Scarlsonj dhcp_pif_t *v4root;
49d04ccbb3Scarlsonj dhcp_pif_t *v6root;
50d04ccbb3Scarlsonj
51d04ccbb3Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
527c478bd9Sstevel@tonic-gate
537c478bd9Sstevel@tonic-gate /*
54d04ccbb3Scarlsonj * Interface flags to watch: things that should be under our direct control.
557c478bd9Sstevel@tonic-gate */
565c0b7edeSseb #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \
575c0b7edeSseb IFF_TEMPORARY)
587c478bd9Sstevel@tonic-gate
59d04ccbb3Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *);
607c478bd9Sstevel@tonic-gate
617c478bd9Sstevel@tonic-gate /*
62d04ccbb3Scarlsonj * insert_pif(): creates a new physical interface structure and chains it on
63d04ccbb3Scarlsonj * the list. Initializes state that remains consistent across
64d04ccbb3Scarlsonj * all use of the physical interface entry.
657c478bd9Sstevel@tonic-gate *
66d04ccbb3Scarlsonj * input: const char *: the name of the physical interface
67d04ccbb3Scarlsonj * boolean_t: if B_TRUE, this is DHCPv6
68d04ccbb3Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
697c478bd9Sstevel@tonic-gate * error code with the reason why
70d04ccbb3Scarlsonj * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
717c478bd9Sstevel@tonic-gate */
727c478bd9Sstevel@tonic-gate
73d04ccbb3Scarlsonj dhcp_pif_t *
insert_pif(const char * pname,boolean_t isv6,int * error)74d04ccbb3Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error)
757c478bd9Sstevel@tonic-gate {
76d04ccbb3Scarlsonj dhcp_pif_t *pif;
77d04ccbb3Scarlsonj struct lifreq lifr;
78e11c3f44Smeem lifgroupinfo_t lifgr;
79e704a8f2Smeem dlpi_handle_t dh = NULL;
80f7d61273Smeem int fd = isv6 ? v6_sock_fd : v4_sock_fd;
817c478bd9Sstevel@tonic-gate
82d04ccbb3Scarlsonj if ((pif = calloc(1, sizeof (*pif))) == NULL) {
83d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for "
84d04ccbb3Scarlsonj "%s", pname);
857c478bd9Sstevel@tonic-gate *error = DHCP_IPC_E_MEMORY;
867c478bd9Sstevel@tonic-gate return (NULL);
877c478bd9Sstevel@tonic-gate }
887c478bd9Sstevel@tonic-gate
89d04ccbb3Scarlsonj pif->pif_isv6 = isv6;
90d04ccbb3Scarlsonj pif->pif_hold_count = 1;
91d04ccbb3Scarlsonj pif->pif_running = B_TRUE;
927c478bd9Sstevel@tonic-gate
93d04ccbb3Scarlsonj if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) {
94d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long",
95d04ccbb3Scarlsonj pname);
967c478bd9Sstevel@tonic-gate *error = DHCP_IPC_E_INVIF;
977c478bd9Sstevel@tonic-gate goto failure;
987c478bd9Sstevel@tonic-gate }
997c478bd9Sstevel@tonic-gate
100f7d61273Smeem /*
101f7d61273Smeem * This is a bit gross, but IP has a confused interface. We must
102f7d61273Smeem * assume that the zeroth LIF is plumbed, and must query there to get
103f7d61273Smeem * the interface index number.
104f7d61273Smeem */
105f7d61273Smeem (void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ);
106f7d61273Smeem
107f7d61273Smeem if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
108f7d61273Smeem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
109f7d61273Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname);
110f7d61273Smeem goto failure;
111f7d61273Smeem }
112f7d61273Smeem pif->pif_index = lifr.lifr_index;
113f7d61273Smeem
114*1cb875aeSCathy Zhou /*
115*1cb875aeSCathy Zhou * Check if this is a VRRP interface. If yes, its IP addresses (the
116*1cb875aeSCathy Zhou * VRRP virtual addresses) cannot be configured using DHCP.
117*1cb875aeSCathy Zhou */
118*1cb875aeSCathy Zhou if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
119*1cb875aeSCathy Zhou *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
120*1cb875aeSCathy Zhou dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFFLAGS for %s", pname);
121*1cb875aeSCathy Zhou goto failure;
122*1cb875aeSCathy Zhou }
123*1cb875aeSCathy Zhou
124*1cb875aeSCathy Zhou if (lifr.lifr_flags & IFF_VRRP) {
125*1cb875aeSCathy Zhou *error = DHCP_IPC_E_INVIF;
126*1cb875aeSCathy Zhou dhcpmsg(MSG_ERROR, "insert_pif: VRRP virtual addresses over %s "
127*1cb875aeSCathy Zhou "cannot be configured using DHCP", pname);
128*1cb875aeSCathy Zhou goto failure;
129*1cb875aeSCathy Zhou }
130*1cb875aeSCathy Zhou
131f7d61273Smeem if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) {
132f7d61273Smeem *error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
133f7d61273Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname);
134f7d61273Smeem goto failure;
135f7d61273Smeem }
136f7d61273Smeem pif->pif_max = lifr.lifr_mtu;
137f7d61273Smeem
138f7d61273Smeem if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
139f7d61273Smeem dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to "
140f7d61273Smeem "support DHCP (%u < %u)", pname, pif->pif_max,
141f7d61273Smeem DHCP_DEF_MAX_SIZE);
142f7d61273Smeem *error = DHCP_IPC_E_INVIF;
143f7d61273Smeem goto failure;
144f7d61273Smeem }
145f7d61273Smeem
146f7d61273Smeem /*
147e11c3f44Smeem * Check if the pif is in an IPMP group. Interfaces using IPMP don't
148e11c3f44Smeem * have dedicated hardware addresses, and get their hardware type from
149e11c3f44Smeem * the SIOCGLIFGROUPINFO ioctl rather than DLPI.
150f7d61273Smeem */
151e11c3f44Smeem if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1) {
152e11c3f44Smeem *error = DHCP_IPC_E_INT;
153e11c3f44Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPNAME for %s", pname);
154e11c3f44Smeem goto failure;
155e11c3f44Smeem }
156e11c3f44Smeem
157e11c3f44Smeem if (lifr.lifr_groupname[0] != '\0') {
158e11c3f44Smeem (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
159e11c3f44Smeem LIFGRNAMSIZ);
160e11c3f44Smeem if (ioctl(fd, SIOCGLIFGROUPINFO, &lifgr) == -1) {
161e11c3f44Smeem *error = DHCP_IPC_E_INT;
162e11c3f44Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPINFO for %s",
163e11c3f44Smeem lifgr.gi_grname);
164e11c3f44Smeem goto failure;
165e11c3f44Smeem }
166e11c3f44Smeem
167e11c3f44Smeem pif->pif_hwtype = dlpi_arptype(lifgr.gi_mactype);
168e11c3f44Smeem pif->pif_under_ipmp = (strcmp(pname, lifgr.gi_grifname) != 0);
169e11c3f44Smeem (void) strlcpy(pif->pif_grifname, lifgr.gi_grifname, LIFNAMSIZ);
170e11c3f44Smeem
171e11c3f44Smeem /*
172e11c3f44Smeem * For IPMP underlying interfaces, stash the interface index
173e11c3f44Smeem * of the IPMP meta-interface; we'll use it to send/receive
174e11c3f44Smeem * traffic. This is both necessary (since IP_BOUND_IF for
175e11c3f44Smeem * non-unicast traffic won't work on underlying interfaces)
176e11c3f44Smeem * and preferred (since a test address lease will be able to
177e11c3f44Smeem * be maintained as long as another interface in the group is
178e11c3f44Smeem * still functioning).
179e11c3f44Smeem */
180e11c3f44Smeem if (pif->pif_under_ipmp) {
181e11c3f44Smeem (void) strlcpy(lifr.lifr_name, pif->pif_grifname,
182e11c3f44Smeem LIFNAMSIZ);
183e11c3f44Smeem
184e11c3f44Smeem if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
185e11c3f44Smeem *error = DHCP_IPC_E_INT;
186e11c3f44Smeem dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX "
187e11c3f44Smeem "for %s", lifr.lifr_name);
188e11c3f44Smeem goto failure;
189e11c3f44Smeem }
190e11c3f44Smeem pif->pif_grindex = lifr.lifr_index;
191e11c3f44Smeem }
192e11c3f44Smeem }
193e11c3f44Smeem
194e11c3f44Smeem /*
195e11c3f44Smeem * For IPv4, if the hardware type is still unknown, use DLPI to
196e11c3f44Smeem * determine it, the hardware address, and hardware address length.
197e11c3f44Smeem */
198e11c3f44Smeem if (!isv6 && pif->pif_hwtype == 0) {
199948f2876Sss150715 int rc;
200948f2876Sss150715 dlpi_info_t dlinfo;
201d04ccbb3Scarlsonj
202948f2876Sss150715 if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) {
203948f2876Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s",
204948f2876Sss150715 dlpi_strerror(rc));
205948f2876Sss150715 *error = DHCP_IPC_E_INVIF;
206948f2876Sss150715 goto failure;
207948f2876Sss150715 }
208948f2876Sss150715
209948f2876Sss150715 if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
210948f2876Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s",
211948f2876Sss150715 dlpi_strerror(rc));
212d04ccbb3Scarlsonj *error = DHCP_IPC_E_INVIF;
213d04ccbb3Scarlsonj goto failure;
214d04ccbb3Scarlsonj }
2157c478bd9Sstevel@tonic-gate
216f7d61273Smeem if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) {
217948f2876Sss150715 dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s",
218948f2876Sss150715 dlpi_strerror(rc));
219948f2876Sss150715 *error = DHCP_IPC_E_INVIF;
220948f2876Sss150715 goto failure;
221948f2876Sss150715 }
222948f2876Sss150715
223948f2876Sss150715 pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype);
224948f2876Sss150715 pif->pif_hwlen = dlinfo.di_physaddrlen;
2257c478bd9Sstevel@tonic-gate
226e704a8f2Smeem dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d",
227e704a8f2Smeem pname, pif->pif_hwtype, pif->pif_hwlen);
2287c478bd9Sstevel@tonic-gate
229d04ccbb3Scarlsonj if (pif->pif_hwlen > 0) {
230d04ccbb3Scarlsonj pif->pif_hwaddr = malloc(pif->pif_hwlen);
231d04ccbb3Scarlsonj if (pif->pif_hwaddr == NULL) {
232d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
233d04ccbb3Scarlsonj "pif_hwaddr for %s", pname);
2347c478bd9Sstevel@tonic-gate *error = DHCP_IPC_E_MEMORY;
2357c478bd9Sstevel@tonic-gate goto failure;
2367c478bd9Sstevel@tonic-gate }
237948f2876Sss150715 (void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
238948f2876Sss150715 pif->pif_hwlen);
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate
241e704a8f2Smeem dlpi_close(dh);
242e704a8f2Smeem dh = NULL;
243d04ccbb3Scarlsonj }
2447c478bd9Sstevel@tonic-gate
245d04ccbb3Scarlsonj insque(pif, isv6 ? &v6root : &v4root);
246d04ccbb3Scarlsonj
247d04ccbb3Scarlsonj return (pif);
248d04ccbb3Scarlsonj failure:
249e704a8f2Smeem if (dh != NULL)
250e704a8f2Smeem dlpi_close(dh);
251d04ccbb3Scarlsonj release_pif(pif);
252d04ccbb3Scarlsonj return (NULL);
253d04ccbb3Scarlsonj }
254d04ccbb3Scarlsonj
255d04ccbb3Scarlsonj /*
256d04ccbb3Scarlsonj * hold_pif(): acquire a hold on a physical interface structure.
257d04ccbb3Scarlsonj *
258d04ccbb3Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure
259d04ccbb3Scarlsonj * output: none
260d04ccbb3Scarlsonj */
261d04ccbb3Scarlsonj
262d04ccbb3Scarlsonj void
hold_pif(dhcp_pif_t * pif)263d04ccbb3Scarlsonj hold_pif(dhcp_pif_t *pif)
264d04ccbb3Scarlsonj {
265d04ccbb3Scarlsonj pif->pif_hold_count++;
266d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
267d04ccbb3Scarlsonj pif->pif_hold_count);
268d04ccbb3Scarlsonj }
269d04ccbb3Scarlsonj
270d04ccbb3Scarlsonj /*
271d04ccbb3Scarlsonj * release_pif(): release a hold on a physical interface structure; will
272d04ccbb3Scarlsonj * destroy the structure on the last hold removed.
273d04ccbb3Scarlsonj *
274d04ccbb3Scarlsonj * input: dhcp_pif_t *: a pointer to the PIF structure
275d04ccbb3Scarlsonj * output: none
276d04ccbb3Scarlsonj */
277d04ccbb3Scarlsonj
278d04ccbb3Scarlsonj void
release_pif(dhcp_pif_t * pif)279d04ccbb3Scarlsonj release_pif(dhcp_pif_t *pif)
280d04ccbb3Scarlsonj {
281d04ccbb3Scarlsonj if (pif->pif_hold_count == 0) {
282d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
283d04ccbb3Scarlsonj return;
284d04ccbb3Scarlsonj }
285d04ccbb3Scarlsonj
286d04ccbb3Scarlsonj if (--pif->pif_hold_count == 0) {
287d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
288d04ccbb3Scarlsonj pif->pif_name);
289d04ccbb3Scarlsonj
290d04ccbb3Scarlsonj remque(pif);
291d04ccbb3Scarlsonj free(pif->pif_hwaddr);
292d04ccbb3Scarlsonj free(pif);
293d04ccbb3Scarlsonj } else {
294d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
295d04ccbb3Scarlsonj pif->pif_name, pif->pif_hold_count);
296d04ccbb3Scarlsonj }
297d04ccbb3Scarlsonj }
298d04ccbb3Scarlsonj
299d04ccbb3Scarlsonj /*
300d04ccbb3Scarlsonj * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
301d04ccbb3Scarlsonj * previous PIF pointer (or NULL for list start).
302d04ccbb3Scarlsonj * Caller is expected to iterate through all
303d04ccbb3Scarlsonj * potential matches to find interface of interest.
304d04ccbb3Scarlsonj *
305d04ccbb3Scarlsonj * input: uint16_t: the interface index (truncated)
306d04ccbb3Scarlsonj * dhcp_pif_t *: the previous PIF, or NULL for list start
307d04ccbb3Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
308d04ccbb3Scarlsonj * output: dhcp_pif_t *: the next matching PIF, or NULL if not found
309d04ccbb3Scarlsonj * note: This operates using the 'truncated' (16-bit) ifindex as seen by
310d04ccbb3Scarlsonj * routing socket clients. The value stored in pif_index is the
311d04ccbb3Scarlsonj * 32-bit ifindex from the ioctl interface.
312d04ccbb3Scarlsonj */
313d04ccbb3Scarlsonj
314d04ccbb3Scarlsonj dhcp_pif_t *
lookup_pif_by_uindex(uint16_t ifindex,dhcp_pif_t * pif,boolean_t isv6)315d04ccbb3Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
316d04ccbb3Scarlsonj {
317d04ccbb3Scarlsonj if (pif == NULL)
318d04ccbb3Scarlsonj pif = isv6 ? v6root : v4root;
319d04ccbb3Scarlsonj else
320d04ccbb3Scarlsonj pif = pif->pif_next;
321d04ccbb3Scarlsonj
322d04ccbb3Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
323d04ccbb3Scarlsonj if ((pif->pif_index & 0xffff) == ifindex)
324d04ccbb3Scarlsonj break;
325d04ccbb3Scarlsonj }
326d04ccbb3Scarlsonj
327d04ccbb3Scarlsonj return (pif);
328d04ccbb3Scarlsonj }
329d04ccbb3Scarlsonj
330d04ccbb3Scarlsonj /*
331d04ccbb3Scarlsonj * lookup_pif_by_name(): Looks up a physical interface entry given a name.
332d04ccbb3Scarlsonj *
333d04ccbb3Scarlsonj * input: const char *: the physical interface name
334d04ccbb3Scarlsonj * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
335d04ccbb3Scarlsonj * output: dhcp_pif_t *: the matching PIF, or NULL if not found
336d04ccbb3Scarlsonj */
337d04ccbb3Scarlsonj
338d04ccbb3Scarlsonj dhcp_pif_t *
lookup_pif_by_name(const char * pname,boolean_t isv6)339d04ccbb3Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6)
340d04ccbb3Scarlsonj {
341d04ccbb3Scarlsonj dhcp_pif_t *pif;
342d04ccbb3Scarlsonj
343d04ccbb3Scarlsonj pif = isv6 ? v6root : v4root;
344d04ccbb3Scarlsonj
345d04ccbb3Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
346d04ccbb3Scarlsonj if (strcmp(pif->pif_name, pname) == 0)
347d04ccbb3Scarlsonj break;
348d04ccbb3Scarlsonj }
349d04ccbb3Scarlsonj
350d04ccbb3Scarlsonj return (pif);
351d04ccbb3Scarlsonj }
352d04ccbb3Scarlsonj
353d04ccbb3Scarlsonj /*
354d04ccbb3Scarlsonj * pif_status(): update the physical interface up/down status.
355d04ccbb3Scarlsonj *
356e704a8f2Smeem * input: dhcp_pif_t *: the physical interface to be updated
357d04ccbb3Scarlsonj * boolean_t: B_TRUE if the interface is going up
358d04ccbb3Scarlsonj * output: none
359d04ccbb3Scarlsonj */
360d04ccbb3Scarlsonj
361d04ccbb3Scarlsonj void
pif_status(dhcp_pif_t * pif,boolean_t isup)362d04ccbb3Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup)
363d04ccbb3Scarlsonj {
364d04ccbb3Scarlsonj dhcp_lif_t *lif;
365d04ccbb3Scarlsonj dhcp_smach_t *dsmp;
366d04ccbb3Scarlsonj
367d04ccbb3Scarlsonj pif->pif_running = isup;
368906cb642Scarlsonj dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name,
369d04ccbb3Scarlsonj isup ? "come back up" : "gone down");
370d04ccbb3Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
371d04ccbb3Scarlsonj for (dsmp = lif->lif_smachs; dsmp != NULL;
372d04ccbb3Scarlsonj dsmp = dsmp->dsm_next) {
373d04ccbb3Scarlsonj if (isup)
374d04ccbb3Scarlsonj refresh_smach(dsmp);
375d04ccbb3Scarlsonj else
376d04ccbb3Scarlsonj remove_default_routes(dsmp);
377d04ccbb3Scarlsonj }
378d04ccbb3Scarlsonj }
379d04ccbb3Scarlsonj }
380d04ccbb3Scarlsonj
381d04ccbb3Scarlsonj /* Helper for insert_lif: extract addresses as defined */
382d04ccbb3Scarlsonj #define ASSIGN_ADDR(v4, v6, lf) \
383d04ccbb3Scarlsonj if (pif->pif_isv6) { \
384d04ccbb3Scarlsonj lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
385d04ccbb3Scarlsonj } else { \
386d04ccbb3Scarlsonj lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
387d04ccbb3Scarlsonj }
388d04ccbb3Scarlsonj
389d04ccbb3Scarlsonj /*
390d04ccbb3Scarlsonj * insert_lif(): Creates a new logical interface structure and chains it on
391d04ccbb3Scarlsonj * the list for a given physical interface. Initializes state
392d04ccbb3Scarlsonj * that remains consistent across all use of the logical
393d04ccbb3Scarlsonj * interface entry. Caller's PIF hold is transferred to the
394d04ccbb3Scarlsonj * LIF on success, and is dropped on failure.
395d04ccbb3Scarlsonj *
396d04ccbb3Scarlsonj * input: dhcp_pif_t *: pointer to the physical interface for this LIF
397d04ccbb3Scarlsonj * const char *: the name of the logical interface
398d04ccbb3Scarlsonj * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
399d04ccbb3Scarlsonj * error code with the reason why
400d04ccbb3Scarlsonj * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
401d04ccbb3Scarlsonj */
402d04ccbb3Scarlsonj
403d04ccbb3Scarlsonj dhcp_lif_t *
insert_lif(dhcp_pif_t * pif,const char * lname,int * error)404d04ccbb3Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
405d04ccbb3Scarlsonj {
406d04ccbb3Scarlsonj dhcp_lif_t *lif;
407d04ccbb3Scarlsonj int fd;
408d04ccbb3Scarlsonj struct lifreq lifr;
409d04ccbb3Scarlsonj
410d04ccbb3Scarlsonj if ((lif = calloc(1, sizeof (*lif))) == NULL) {
411d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
412d04ccbb3Scarlsonj "%s", lname);
413d04ccbb3Scarlsonj *error = DHCP_IPC_E_MEMORY;
414d04ccbb3Scarlsonj return (NULL);
415d04ccbb3Scarlsonj }
416d04ccbb3Scarlsonj
417d04ccbb3Scarlsonj lif->lif_sock_ip_fd = -1;
418e704a8f2Smeem lif->lif_packet_id = -1;
419d04ccbb3Scarlsonj lif->lif_iaid_id = -1;
420d04ccbb3Scarlsonj lif->lif_hold_count = 1;
421d04ccbb3Scarlsonj lif->lif_pif = pif;
422d04ccbb3Scarlsonj lif->lif_removed = B_TRUE;
423d04ccbb3Scarlsonj init_timer(&lif->lif_preferred, 0);
424d04ccbb3Scarlsonj init_timer(&lif->lif_expire, 0);
425d04ccbb3Scarlsonj
426d04ccbb3Scarlsonj if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
427d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
428d04ccbb3Scarlsonj lname);
429d04ccbb3Scarlsonj *error = DHCP_IPC_E_INVIF;
430d04ccbb3Scarlsonj goto failure;
431d04ccbb3Scarlsonj }
432d04ccbb3Scarlsonj
433d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
434d04ccbb3Scarlsonj
435d04ccbb3Scarlsonj fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
436d04ccbb3Scarlsonj
437d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
438d04ccbb3Scarlsonj lif->lif_max = 1024;
439d04ccbb3Scarlsonj else
440d04ccbb3Scarlsonj lif->lif_max = lifr.lifr_mtu;
441d04ccbb3Scarlsonj
442d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
4437c478bd9Sstevel@tonic-gate if (errno == ENXIO)
4447c478bd9Sstevel@tonic-gate *error = DHCP_IPC_E_INVIF;
4457c478bd9Sstevel@tonic-gate else
4467c478bd9Sstevel@tonic-gate *error = DHCP_IPC_E_INT;
447d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
4487c478bd9Sstevel@tonic-gate goto failure;
4497c478bd9Sstevel@tonic-gate }
450d04ccbb3Scarlsonj ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
451d04ccbb3Scarlsonj
452d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
453d04ccbb3Scarlsonj if (errno == ENXIO)
454d04ccbb3Scarlsonj *error = DHCP_IPC_E_INVIF;
455d04ccbb3Scarlsonj else
456d04ccbb3Scarlsonj *error = DHCP_IPC_E_INT;
457d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
458d04ccbb3Scarlsonj goto failure;
459d04ccbb3Scarlsonj }
460d04ccbb3Scarlsonj ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
461d04ccbb3Scarlsonj
462d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
463d04ccbb3Scarlsonj *error = DHCP_IPC_E_INT;
464d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
465d04ccbb3Scarlsonj goto failure;
466d04ccbb3Scarlsonj }
467d04ccbb3Scarlsonj lif->lif_flags = lifr.lifr_flags;
468d04ccbb3Scarlsonj
469d04ccbb3Scarlsonj /*
470d04ccbb3Scarlsonj * If we've just detected the interface going up or down, then signal
471d04ccbb3Scarlsonj * an appropriate action. There may be other state machines here.
472d04ccbb3Scarlsonj */
473d04ccbb3Scarlsonj if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
474d04ccbb3Scarlsonj pif_status(pif, B_TRUE);
475d04ccbb3Scarlsonj } else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
476d04ccbb3Scarlsonj pif_status(pif, B_FALSE);
477d04ccbb3Scarlsonj }
478d04ccbb3Scarlsonj
479d04ccbb3Scarlsonj if (lifr.lifr_flags & IFF_POINTOPOINT) {
480d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
481d04ccbb3Scarlsonj *error = DHCP_IPC_E_INT;
482d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
483d04ccbb3Scarlsonj lname);
484d04ccbb3Scarlsonj goto failure;
485d04ccbb3Scarlsonj }
486d04ccbb3Scarlsonj ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
487d04ccbb3Scarlsonj } else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
488d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
489d04ccbb3Scarlsonj *error = DHCP_IPC_E_INT;
490d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
491d04ccbb3Scarlsonj lname);
492d04ccbb3Scarlsonj goto failure;
493d04ccbb3Scarlsonj }
494d04ccbb3Scarlsonj lif->lif_broadcast =
495d04ccbb3Scarlsonj ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
496d04ccbb3Scarlsonj s_addr;
497d04ccbb3Scarlsonj }
498d04ccbb3Scarlsonj
499d04ccbb3Scarlsonj if (pif->pif_isv6)
500d04ccbb3Scarlsonj cached_v6_max_mtu = 0;
501d04ccbb3Scarlsonj else
502d04ccbb3Scarlsonj cached_v4_max_mtu = 0;
503d04ccbb3Scarlsonj
504d04ccbb3Scarlsonj lif->lif_removed = B_FALSE;
505d04ccbb3Scarlsonj insque(lif, &pif->pif_lifs);
506d04ccbb3Scarlsonj
507d04ccbb3Scarlsonj return (lif);
508d04ccbb3Scarlsonj
509d04ccbb3Scarlsonj failure:
510d04ccbb3Scarlsonj release_lif(lif);
511d04ccbb3Scarlsonj return (NULL);
512d04ccbb3Scarlsonj }
513d04ccbb3Scarlsonj
514d04ccbb3Scarlsonj /*
515d04ccbb3Scarlsonj * hold_lif(): acquire a hold on a logical interface structure.
516d04ccbb3Scarlsonj *
517d04ccbb3Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure
518d04ccbb3Scarlsonj * output: none
519d04ccbb3Scarlsonj */
520d04ccbb3Scarlsonj
521d04ccbb3Scarlsonj void
hold_lif(dhcp_lif_t * lif)522d04ccbb3Scarlsonj hold_lif(dhcp_lif_t *lif)
523d04ccbb3Scarlsonj {
524d04ccbb3Scarlsonj lif->lif_hold_count++;
525d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
526d04ccbb3Scarlsonj lif->lif_hold_count);
527d04ccbb3Scarlsonj }
528d04ccbb3Scarlsonj
529d04ccbb3Scarlsonj /*
530d04ccbb3Scarlsonj * release_lif(): release a hold on a logical interface structure; will
531d04ccbb3Scarlsonj * destroy the structure on the last hold removed.
532d04ccbb3Scarlsonj *
533d04ccbb3Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure
534d04ccbb3Scarlsonj * output: none
535d04ccbb3Scarlsonj */
536d04ccbb3Scarlsonj
537d04ccbb3Scarlsonj void
release_lif(dhcp_lif_t * lif)538d04ccbb3Scarlsonj release_lif(dhcp_lif_t *lif)
539d04ccbb3Scarlsonj {
540d04ccbb3Scarlsonj if (lif->lif_hold_count == 0) {
541d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
542d04ccbb3Scarlsonj lif->lif_name);
543d04ccbb3Scarlsonj return;
544d04ccbb3Scarlsonj }
545d04ccbb3Scarlsonj
546d04ccbb3Scarlsonj if (lif->lif_hold_count == 1 && !lif->lif_removed) {
547d04ccbb3Scarlsonj unplumb_lif(lif);
548d04ccbb3Scarlsonj return;
549d04ccbb3Scarlsonj }
550d04ccbb3Scarlsonj
551d04ccbb3Scarlsonj if (--lif->lif_hold_count == 0) {
552d04ccbb3Scarlsonj dhcp_pif_t *pif;
553d04ccbb3Scarlsonj
554d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
555d04ccbb3Scarlsonj lif->lif_name);
556d04ccbb3Scarlsonj
557d04ccbb3Scarlsonj if (lif->lif_lease != NULL)
558d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT,
559d04ccbb3Scarlsonj "release_lif: still holding lease at last hold!");
560d04ccbb3Scarlsonj close_ip_lif(lif);
561d04ccbb3Scarlsonj pif = lif->lif_pif;
562d04ccbb3Scarlsonj if (pif->pif_isv6)
563d04ccbb3Scarlsonj cached_v6_max_mtu = 0;
564d04ccbb3Scarlsonj else
565d04ccbb3Scarlsonj cached_v4_max_mtu = 0;
566d04ccbb3Scarlsonj release_pif(pif);
567d04ccbb3Scarlsonj free(lif);
568d04ccbb3Scarlsonj } else {
569d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
570d04ccbb3Scarlsonj lif->lif_name, lif->lif_hold_count);
571d04ccbb3Scarlsonj }
572d04ccbb3Scarlsonj }
573d04ccbb3Scarlsonj
574d04ccbb3Scarlsonj /*
575d04ccbb3Scarlsonj * remove_lif(): remove a logical interface from its PIF and lease (if any) and
576d04ccbb3Scarlsonj * the lease's hold on the LIF. Assumes that we did not plumb
577d04ccbb3Scarlsonj * the interface.
578d04ccbb3Scarlsonj *
579d04ccbb3Scarlsonj * input: dhcp_lif_t *: a pointer to the LIF structure
580d04ccbb3Scarlsonj * output: none
581d04ccbb3Scarlsonj */
582d04ccbb3Scarlsonj
583d04ccbb3Scarlsonj void
remove_lif(dhcp_lif_t * lif)584d04ccbb3Scarlsonj remove_lif(dhcp_lif_t *lif)
585d04ccbb3Scarlsonj {
586d04ccbb3Scarlsonj if (lif->lif_plumbed) {
587d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
588d04ccbb3Scarlsonj lif->lif_name);
589d04ccbb3Scarlsonj return;
590d04ccbb3Scarlsonj }
591d04ccbb3Scarlsonj if (lif->lif_removed) {
592d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
593d04ccbb3Scarlsonj lif->lif_name);
594d04ccbb3Scarlsonj } else {
595d04ccbb3Scarlsonj dhcp_lif_t *lifnext;
596d04ccbb3Scarlsonj dhcp_lease_t *dlp;
597d04ccbb3Scarlsonj
598d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
599d04ccbb3Scarlsonj lif->lif_removed = B_TRUE;
600d04ccbb3Scarlsonj lifnext = lif->lif_next;
601d04ccbb3Scarlsonj clear_lif_dhcp(lif);
602d04ccbb3Scarlsonj cancel_lif_timers(lif);
603d04ccbb3Scarlsonj if (lif->lif_iaid_id != -1 &&
604d04ccbb3Scarlsonj iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
605d04ccbb3Scarlsonj lif->lif_iaid_id = -1;
606d04ccbb3Scarlsonj release_lif(lif);
607d04ccbb3Scarlsonj }
608d04ccbb3Scarlsonj
609d04ccbb3Scarlsonj /* Remove from PIF list */
610d04ccbb3Scarlsonj remque(lif);
611d04ccbb3Scarlsonj
612d04ccbb3Scarlsonj /* If we were part of a lease, then remove ourselves */
613d04ccbb3Scarlsonj if ((dlp = lif->lif_lease) != NULL) {
614d04ccbb3Scarlsonj if (--dlp->dl_nlifs == 0)
615d04ccbb3Scarlsonj dlp->dl_lifs = NULL;
616d04ccbb3Scarlsonj else if (dlp->dl_lifs == lif)
617d04ccbb3Scarlsonj dlp->dl_lifs = lifnext;
618d04ccbb3Scarlsonj if (lif->lif_declined != NULL) {
619d04ccbb3Scarlsonj dlp->dl_smach->dsm_lif_down--;
620d04ccbb3Scarlsonj lif->lif_declined = NULL;
621d04ccbb3Scarlsonj }
62242dc071eSJames Carlson if (lif->lif_dad_wait) {
62342dc071eSJames Carlson lif->lif_dad_wait = _B_FALSE;
62442dc071eSJames Carlson dlp->dl_smach->dsm_lif_wait--;
62542dc071eSJames Carlson }
626d04ccbb3Scarlsonj lif->lif_lease = NULL;
627d04ccbb3Scarlsonj release_lif(lif);
628d04ccbb3Scarlsonj }
629d04ccbb3Scarlsonj }
630d04ccbb3Scarlsonj }
631d04ccbb3Scarlsonj
632d04ccbb3Scarlsonj /*
633d04ccbb3Scarlsonj * lookup_lif_by_name(): Looks up a logical interface entry given a name and
634d04ccbb3Scarlsonj * a physical interface.
635d04ccbb3Scarlsonj *
636d04ccbb3Scarlsonj * input: const char *: the logical interface name
637d04ccbb3Scarlsonj * const dhcp_pif_t *: the physical interface
638d04ccbb3Scarlsonj * output: dhcp_lif_t *: the matching LIF, or NULL if not found
639d04ccbb3Scarlsonj */
640d04ccbb3Scarlsonj
641d04ccbb3Scarlsonj dhcp_lif_t *
lookup_lif_by_name(const char * lname,const dhcp_pif_t * pif)642d04ccbb3Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
643d04ccbb3Scarlsonj {
644d04ccbb3Scarlsonj dhcp_lif_t *lif;
645d04ccbb3Scarlsonj
646d04ccbb3Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
647d04ccbb3Scarlsonj if (strcmp(lif->lif_name, lname) == 0)
648d04ccbb3Scarlsonj break;
649d04ccbb3Scarlsonj }
650d04ccbb3Scarlsonj
651d04ccbb3Scarlsonj return (lif);
652d04ccbb3Scarlsonj }
653d04ccbb3Scarlsonj
654d04ccbb3Scarlsonj /*
655d04ccbb3Scarlsonj * checkaddr(): checks if the given address is still set on the given LIF
656d04ccbb3Scarlsonj *
657d04ccbb3Scarlsonj * input: const dhcp_lif_t *: the LIF to check
658d04ccbb3Scarlsonj * int: the address to look up on the interface (ioctl)
659d04ccbb3Scarlsonj * const in6_addr_t *: the address to compare to
660d04ccbb3Scarlsonj * const char *: name of the address for logging purposes
661d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
662d04ccbb3Scarlsonj */
663d04ccbb3Scarlsonj
664d04ccbb3Scarlsonj static boolean_t
checkaddr(const dhcp_lif_t * lif,int ioccmd,const in6_addr_t * addr,const char * aname)665d04ccbb3Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
666d04ccbb3Scarlsonj const char *aname)
667d04ccbb3Scarlsonj {
668d04ccbb3Scarlsonj boolean_t isv6;
669d04ccbb3Scarlsonj int fd;
670d04ccbb3Scarlsonj struct lifreq lifr;
671e704a8f2Smeem char abuf1[INET6_ADDRSTRLEN];
672e704a8f2Smeem char abuf2[INET6_ADDRSTRLEN];
673d04ccbb3Scarlsonj
674d04ccbb3Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq));
675d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
676d04ccbb3Scarlsonj
677d04ccbb3Scarlsonj isv6 = lif->lif_pif->pif_isv6;
678d04ccbb3Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd;
679d04ccbb3Scarlsonj
680d04ccbb3Scarlsonj if (ioctl(fd, ioccmd, &lifr) == -1) {
681d04ccbb3Scarlsonj if (errno == ENXIO) {
682d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
683d04ccbb3Scarlsonj lif->lif_name);
684d04ccbb3Scarlsonj return (B_FALSE);
685d04ccbb3Scarlsonj }
686d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG,
687d04ccbb3Scarlsonj "checkaddr: ignoring ioctl error on %s %x: %s",
688d04ccbb3Scarlsonj lif->lif_name, ioccmd, strerror(errno));
689d04ccbb3Scarlsonj } else if (isv6) {
690d04ccbb3Scarlsonj struct sockaddr_in6 *sin6 =
691d04ccbb3Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr;
692d04ccbb3Scarlsonj
693d04ccbb3Scarlsonj if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
694d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
695e704a8f2Smeem "checkaddr: expected %s %s on %s, have %s", aname,
696e704a8f2Smeem inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)),
697e704a8f2Smeem lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr,
698e704a8f2Smeem abuf2, sizeof (abuf2)));
699d04ccbb3Scarlsonj return (B_FALSE);
700d04ccbb3Scarlsonj }
701d04ccbb3Scarlsonj } else {
702d04ccbb3Scarlsonj struct sockaddr_in *sinp =
703d04ccbb3Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr;
704d04ccbb3Scarlsonj ipaddr_t v4addr;
705d04ccbb3Scarlsonj
706d04ccbb3Scarlsonj IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
707d04ccbb3Scarlsonj if (sinp->sin_addr.s_addr != v4addr) {
708d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
709e704a8f2Smeem "checkaddr: expected %s %s on %s, have %s", aname,
710e704a8f2Smeem inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)),
711e704a8f2Smeem lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr,
712e704a8f2Smeem abuf2, sizeof (abuf2)));
713d04ccbb3Scarlsonj return (B_FALSE);
714d04ccbb3Scarlsonj }
715d04ccbb3Scarlsonj }
716d04ccbb3Scarlsonj return (B_TRUE);
717d04ccbb3Scarlsonj }
718d04ccbb3Scarlsonj
719d04ccbb3Scarlsonj /*
720d04ccbb3Scarlsonj * verify_lif(): verifies than a LIF is still valid (i.e., has not been
721d04ccbb3Scarlsonj * explicitly or implicitly dropped or released)
722d04ccbb3Scarlsonj *
723d04ccbb3Scarlsonj * input: const dhcp_lif_t *: the LIF to verify
724d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
725d04ccbb3Scarlsonj */
726d04ccbb3Scarlsonj
727d04ccbb3Scarlsonj boolean_t
verify_lif(const dhcp_lif_t * lif)728d04ccbb3Scarlsonj verify_lif(const dhcp_lif_t *lif)
729d04ccbb3Scarlsonj {
730d04ccbb3Scarlsonj boolean_t isv6;
731d04ccbb3Scarlsonj int fd;
732d04ccbb3Scarlsonj struct lifreq lifr;
733e11c3f44Smeem dhcp_pif_t *pif = lif->lif_pif;
734d04ccbb3Scarlsonj
735d04ccbb3Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq));
736d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
737d04ccbb3Scarlsonj
738e11c3f44Smeem isv6 = pif->pif_isv6;
739d04ccbb3Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd;
740d04ccbb3Scarlsonj
741d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
742cfb9c9abScarlsonj if (errno != ENXIO) {
743cfb9c9abScarlsonj dhcpmsg(MSG_ERR,
744cfb9c9abScarlsonj "verify_lif: SIOCGLIFFLAGS failed on %s",
745d04ccbb3Scarlsonj lif->lif_name);
746cfb9c9abScarlsonj }
747d04ccbb3Scarlsonj return (B_FALSE);
748d04ccbb3Scarlsonj }
749d04ccbb3Scarlsonj
750d04ccbb3Scarlsonj /*
751d04ccbb3Scarlsonj * If important flags have changed, then abandon the interface.
752d04ccbb3Scarlsonj */
753d04ccbb3Scarlsonj if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
754d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
755d04ccbb3Scarlsonj "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
756d04ccbb3Scarlsonj lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
757d04ccbb3Scarlsonj DHCP_IFF_WATCH);
758d04ccbb3Scarlsonj return (B_FALSE);
759d04ccbb3Scarlsonj }
760d04ccbb3Scarlsonj
761d04ccbb3Scarlsonj /*
762d04ccbb3Scarlsonj * Check for delete and recreate.
763d04ccbb3Scarlsonj */
764d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
765e11c3f44Smeem if (errno != ENXIO) {
766e11c3f44Smeem dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed "
767e11c3f44Smeem "on %s", lif->lif_name);
768e11c3f44Smeem }
769d04ccbb3Scarlsonj return (B_FALSE);
770d04ccbb3Scarlsonj }
771e11c3f44Smeem if (lifr.lifr_index != pif->pif_index) {
772d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG,
773d04ccbb3Scarlsonj "verify_lif: ifindex on %s changed: %u to %u",
774e11c3f44Smeem lif->lif_name, pif->pif_index, lifr.lifr_index);
775d04ccbb3Scarlsonj return (B_FALSE);
776d04ccbb3Scarlsonj }
777d04ccbb3Scarlsonj
778e11c3f44Smeem if (pif->pif_under_ipmp) {
779e11c3f44Smeem (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
780e11c3f44Smeem
781e11c3f44Smeem if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
782e11c3f44Smeem if (errno != ENXIO) {
783e11c3f44Smeem dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX "
784e11c3f44Smeem "failed on %s", lifr.lifr_name);
785e11c3f44Smeem }
786e11c3f44Smeem return (B_FALSE);
787e11c3f44Smeem }
788e11c3f44Smeem
789e11c3f44Smeem if (lifr.lifr_index != pif->pif_grindex) {
790e11c3f44Smeem dhcpmsg(MSG_DEBUG, "verify_lif: IPMP group ifindex "
791e11c3f44Smeem "on %s changed: %u to %u", lifr.lifr_name,
792e11c3f44Smeem pif->pif_grindex, lifr.lifr_index);
793e11c3f44Smeem return (B_FALSE);
794e11c3f44Smeem }
795e11c3f44Smeem }
796e11c3f44Smeem
797d04ccbb3Scarlsonj /*
798d04ccbb3Scarlsonj * If the IP address, netmask, or broadcast address have changed, or
799d04ccbb3Scarlsonj * the interface has been unplumbed, then we act like there has been an
800d04ccbb3Scarlsonj * implicit drop. (Note that the netmask is under DHCP control for
801d04ccbb3Scarlsonj * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
802d04ccbb3Scarlsonj * addresses.)
803d04ccbb3Scarlsonj */
804d04ccbb3Scarlsonj
805d04ccbb3Scarlsonj if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
806d04ccbb3Scarlsonj return (B_FALSE);
807d04ccbb3Scarlsonj
808d04ccbb3Scarlsonj if (isv6) {
809d04ccbb3Scarlsonj /*
810d04ccbb3Scarlsonj * If it's not point-to-point, we're done. If it is, then
811d04ccbb3Scarlsonj * check the peer's address as well.
812d04ccbb3Scarlsonj */
813d04ccbb3Scarlsonj return (!(lif->lif_flags & IFF_POINTOPOINT) ||
814d04ccbb3Scarlsonj checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
815d04ccbb3Scarlsonj "peer address"));
816d04ccbb3Scarlsonj } else {
817d04ccbb3Scarlsonj if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
818d04ccbb3Scarlsonj "netmask"))
819d04ccbb3Scarlsonj return (B_FALSE);
820d04ccbb3Scarlsonj
821d04ccbb3Scarlsonj return (checkaddr(lif,
822d04ccbb3Scarlsonj (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
823d04ccbb3Scarlsonj SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
824d04ccbb3Scarlsonj }
825d04ccbb3Scarlsonj }
826d04ccbb3Scarlsonj
827d04ccbb3Scarlsonj /*
828d04ccbb3Scarlsonj * canonize_lif(): puts the interface in a canonical (zeroed) form. This is
829d04ccbb3Scarlsonj * used only on the "main" LIF for IPv4. All other interfaces
830d04ccbb3Scarlsonj * are under dhcpagent control and are removed using
831d04ccbb3Scarlsonj * unplumb_lif().
832d04ccbb3Scarlsonj *
833d04ccbb3Scarlsonj * input: dhcp_lif_t *: the interface to canonize
834e704a8f2Smeem * boolean_t: only canonize lif if it's under DHCP control
835d04ccbb3Scarlsonj * output: none
836d04ccbb3Scarlsonj */
837d04ccbb3Scarlsonj
838d04ccbb3Scarlsonj static void
canonize_lif(dhcp_lif_t * lif,boolean_t dhcponly)839e704a8f2Smeem canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly)
840d04ccbb3Scarlsonj {
841d04ccbb3Scarlsonj boolean_t isv6;
842d04ccbb3Scarlsonj int fd;
843d04ccbb3Scarlsonj struct lifreq lifr;
844d04ccbb3Scarlsonj
845d04ccbb3Scarlsonj /*
846d04ccbb3Scarlsonj * If there's nothing here, then don't touch the interface. This can
847d04ccbb3Scarlsonj * happen when an already-canonized LIF is recanonized.
848d04ccbb3Scarlsonj */
849d04ccbb3Scarlsonj if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
850d04ccbb3Scarlsonj return;
851d04ccbb3Scarlsonj
852d04ccbb3Scarlsonj isv6 = lif->lif_pif->pif_isv6;
853d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
854d04ccbb3Scarlsonj isv6 ? 6 : 4, lif->lif_name);
855d04ccbb3Scarlsonj
856d04ccbb3Scarlsonj lif->lif_v6addr = my_in6addr_any;
857d04ccbb3Scarlsonj lif->lif_v6mask = my_in6addr_any;
858d04ccbb3Scarlsonj lif->lif_v6peer = my_in6addr_any;
859d04ccbb3Scarlsonj
860d04ccbb3Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq));
861d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
862d04ccbb3Scarlsonj
863d04ccbb3Scarlsonj fd = isv6 ? v6_sock_fd : v4_sock_fd;
864d04ccbb3Scarlsonj
865d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
866cfb9c9abScarlsonj if (errno != ENXIO) {
867d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
868d04ccbb3Scarlsonj lif->lif_name);
869cfb9c9abScarlsonj }
870d04ccbb3Scarlsonj return;
871d04ccbb3Scarlsonj }
8727da2140cSmeem lif->lif_flags = lifr.lifr_flags;
873d04ccbb3Scarlsonj
874e704a8f2Smeem if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) {
875d04ccbb3Scarlsonj dhcpmsg(MSG_INFO,
876d04ccbb3Scarlsonj "canonize_lif: cannot clear %s; flags are %llx",
877d04ccbb3Scarlsonj lif->lif_name, lifr.lifr_flags);
878d04ccbb3Scarlsonj return;
879d04ccbb3Scarlsonj }
880d04ccbb3Scarlsonj
881d04ccbb3Scarlsonj (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
882d04ccbb3Scarlsonj if (isv6) {
883d04ccbb3Scarlsonj struct sockaddr_in6 *sin6 =
884d04ccbb3Scarlsonj (struct sockaddr_in6 *)&lifr.lifr_addr;
885d04ccbb3Scarlsonj
886d04ccbb3Scarlsonj sin6->sin6_family = AF_INET6;
887d04ccbb3Scarlsonj sin6->sin6_addr = my_in6addr_any;
888d04ccbb3Scarlsonj } else {
889d04ccbb3Scarlsonj struct sockaddr_in *sinv =
890d04ccbb3Scarlsonj (struct sockaddr_in *)&lifr.lifr_addr;
891d04ccbb3Scarlsonj
892d04ccbb3Scarlsonj sinv->sin_family = AF_INET;
893d04ccbb3Scarlsonj sinv->sin_addr.s_addr = htonl(INADDR_ANY);
894d04ccbb3Scarlsonj }
895d04ccbb3Scarlsonj
896d04ccbb3Scarlsonj if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
897d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
898d04ccbb3Scarlsonj "canonize_lif: can't clear local address on %s",
899d04ccbb3Scarlsonj lif->lif_name);
900d04ccbb3Scarlsonj }
901d04ccbb3Scarlsonj
90242dc071eSJames Carlson /* Clearing the address means that we're no longer waiting on DAD */
90342dc071eSJames Carlson if (lif->lif_dad_wait) {
90442dc071eSJames Carlson lif->lif_dad_wait = _B_FALSE;
90542dc071eSJames Carlson lif->lif_lease->dl_smach->dsm_lif_wait--;
90642dc071eSJames Carlson }
90742dc071eSJames Carlson
908d04ccbb3Scarlsonj if (lif->lif_flags & IFF_POINTOPOINT) {
909d04ccbb3Scarlsonj if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
910d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
911d04ccbb3Scarlsonj "canonize_lif: can't clear remote address on %s",
912d04ccbb3Scarlsonj lif->lif_name);
913d04ccbb3Scarlsonj }
914d04ccbb3Scarlsonj } else if (!isv6) {
915d04ccbb3Scarlsonj if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
916d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
917d04ccbb3Scarlsonj "canonize_lif: can't clear broadcast address on %s",
918d04ccbb3Scarlsonj lif->lif_name);
919d04ccbb3Scarlsonj }
920d04ccbb3Scarlsonj }
921103dd7a7SAnurag S. Maskey
922103dd7a7SAnurag S. Maskey /*
923103dd7a7SAnurag S. Maskey * Clear the netmask last as it has to be refetched after clearing.
924103dd7a7SAnurag S. Maskey * Netmask is under in.ndpd control with IPv6.
925103dd7a7SAnurag S. Maskey */
926103dd7a7SAnurag S. Maskey if (!isv6) {
927103dd7a7SAnurag S. Maskey /* Clear the netmask */
928103dd7a7SAnurag S. Maskey if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
929103dd7a7SAnurag S. Maskey dhcpmsg(MSG_ERR,
930103dd7a7SAnurag S. Maskey "canonize_lif: can't clear netmask on %s",
931103dd7a7SAnurag S. Maskey lif->lif_name);
932103dd7a7SAnurag S. Maskey } else {
933103dd7a7SAnurag S. Maskey /*
934103dd7a7SAnurag S. Maskey * When the netmask is cleared, the kernel actually sets
935103dd7a7SAnurag S. Maskey * the netmask to 255.0.0.0. So, refetch that netmask.
936103dd7a7SAnurag S. Maskey */
937103dd7a7SAnurag S. Maskey if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
938103dd7a7SAnurag S. Maskey dhcpmsg(MSG_ERR,
939103dd7a7SAnurag S. Maskey "canonize_lif: can't reload cleared "
940103dd7a7SAnurag S. Maskey "netmask on %s", lif->lif_name);
941103dd7a7SAnurag S. Maskey } else {
942103dd7a7SAnurag S. Maskey /* Refetch succeeded, update LIF */
943103dd7a7SAnurag S. Maskey lif->lif_netmask =
944103dd7a7SAnurag S. Maskey ((struct sockaddr_in *)&lifr.lifr_addr)->
945103dd7a7SAnurag S. Maskey sin_addr.s_addr;
946103dd7a7SAnurag S. Maskey }
947103dd7a7SAnurag S. Maskey }
948103dd7a7SAnurag S. Maskey }
949d04ccbb3Scarlsonj }
950d04ccbb3Scarlsonj
951d04ccbb3Scarlsonj /*
952d04ccbb3Scarlsonj * plumb_lif(): Adds the LIF to the system. This is used for all
953d04ccbb3Scarlsonj * DHCPv6-derived interfaces. The returned LIF has a hold
95442dc071eSJames Carlson * on it. The caller (configure_v6_leases) deals with the DAD
95542dc071eSJames Carlson * wait counters.
956d04ccbb3Scarlsonj *
957d04ccbb3Scarlsonj * input: dhcp_lif_t *: the interface to unplumb
958d04ccbb3Scarlsonj * output: none
959d04ccbb3Scarlsonj */
960d04ccbb3Scarlsonj
961d04ccbb3Scarlsonj dhcp_lif_t *
plumb_lif(dhcp_pif_t * pif,const in6_addr_t * addr)962d04ccbb3Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
963d04ccbb3Scarlsonj {
964d04ccbb3Scarlsonj dhcp_lif_t *lif;
965d04ccbb3Scarlsonj char abuf[INET6_ADDRSTRLEN];
966d04ccbb3Scarlsonj struct lifreq lifr;
967d04ccbb3Scarlsonj struct sockaddr_in6 *sin6;
968d04ccbb3Scarlsonj int error;
969d04ccbb3Scarlsonj
970d04ccbb3Scarlsonj (void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
971d04ccbb3Scarlsonj
972d04ccbb3Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
973d04ccbb3Scarlsonj if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
974d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
975d04ccbb3Scarlsonj "plumb_lif: entry for %s already exists!", abuf);
976d04ccbb3Scarlsonj return (NULL);
977d04ccbb3Scarlsonj }
978d04ccbb3Scarlsonj }
979d04ccbb3Scarlsonj
980d04ccbb3Scarlsonj /* First, create a new zero-address logical interface */
981d04ccbb3Scarlsonj (void) memset(&lifr, 0, sizeof (lifr));
982d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
983d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
984d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
985d04ccbb3Scarlsonj return (NULL);
986d04ccbb3Scarlsonj }
987d04ccbb3Scarlsonj
988d04ccbb3Scarlsonj /* Next, set the netmask to all ones */
989d04ccbb3Scarlsonj sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
990d04ccbb3Scarlsonj sin6->sin6_family = AF_INET6;
991d04ccbb3Scarlsonj (void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
992d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
993d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
994d04ccbb3Scarlsonj lifr.lifr_name);
995d04ccbb3Scarlsonj goto failure;
996d04ccbb3Scarlsonj }
997d04ccbb3Scarlsonj
998d04ccbb3Scarlsonj /* Now set the interface address */
999d04ccbb3Scarlsonj sin6->sin6_addr = *addr;
1000d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1001d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
1002d04ccbb3Scarlsonj lifr.lifr_name, abuf);
1003d04ccbb3Scarlsonj goto failure;
1004d04ccbb3Scarlsonj }
1005d04ccbb3Scarlsonj
1006d04ccbb3Scarlsonj /* Mark the interface up */
1007d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1008d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
1009d04ccbb3Scarlsonj lifr.lifr_name);
1010d04ccbb3Scarlsonj goto failure;
1011d04ccbb3Scarlsonj }
1012e11c3f44Smeem
1013e11c3f44Smeem /*
1014e11c3f44Smeem * See comment in set_lif_dhcp().
1015e11c3f44Smeem */
1016e11c3f44Smeem if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
1017e11c3f44Smeem lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
1018e11c3f44Smeem
1019d04ccbb3Scarlsonj lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
1020d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1021d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
1022d04ccbb3Scarlsonj lifr.lifr_name);
1023d04ccbb3Scarlsonj goto failure;
1024d04ccbb3Scarlsonj }
1025d04ccbb3Scarlsonj
1026d04ccbb3Scarlsonj /* Now we can create the internal LIF structure */
1027d04ccbb3Scarlsonj hold_pif(pif);
1028d04ccbb3Scarlsonj if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
1029d04ccbb3Scarlsonj goto failure;
1030d04ccbb3Scarlsonj
1031d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
1032d04ccbb3Scarlsonj lif->lif_name);
1033d04ccbb3Scarlsonj lif->lif_plumbed = B_TRUE;
1034d04ccbb3Scarlsonj
1035d04ccbb3Scarlsonj return (lif);
1036d04ccbb3Scarlsonj
1037d04ccbb3Scarlsonj failure:
1038d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
1039d04ccbb3Scarlsonj errno != ENXIO) {
1040d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
1041d04ccbb3Scarlsonj lifr.lifr_name);
1042d04ccbb3Scarlsonj }
1043d04ccbb3Scarlsonj return (NULL);
1044d04ccbb3Scarlsonj }
1045d04ccbb3Scarlsonj
1046d04ccbb3Scarlsonj /*
1047d04ccbb3Scarlsonj * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used
1048d04ccbb3Scarlsonj * for all interfaces configured by DHCP (those in leases).
1049d04ccbb3Scarlsonj *
1050d04ccbb3Scarlsonj * input: dhcp_lif_t *: the interface to unplumb
1051d04ccbb3Scarlsonj * output: none
1052d04ccbb3Scarlsonj */
1053d04ccbb3Scarlsonj
1054d04ccbb3Scarlsonj void
unplumb_lif(dhcp_lif_t * lif)1055d04ccbb3Scarlsonj unplumb_lif(dhcp_lif_t *lif)
1056d04ccbb3Scarlsonj {
1057d04ccbb3Scarlsonj dhcp_lease_t *dlp;
1058d04ccbb3Scarlsonj
1059d04ccbb3Scarlsonj if (lif->lif_plumbed) {
1060d04ccbb3Scarlsonj struct lifreq lifr;
1061d04ccbb3Scarlsonj
1062d04ccbb3Scarlsonj (void) memset(&lifr, 0, sizeof (lifr));
1063d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name,
1064d04ccbb3Scarlsonj sizeof (lifr.lifr_name));
1065d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
1066d04ccbb3Scarlsonj errno != ENXIO) {
1067d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
1068d04ccbb3Scarlsonj lif->lif_name);
1069d04ccbb3Scarlsonj }
1070d04ccbb3Scarlsonj lif->lif_plumbed = B_FALSE;
1071d04ccbb3Scarlsonj }
1072f7d61273Smeem
1073d04ccbb3Scarlsonj /*
1074d04ccbb3Scarlsonj * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
107542dc071eSJames Carlson * just canonize it and remove it from the lease. The DAD wait flags
107642dc071eSJames Carlson * are handled by canonize_lif or by remove_lif.
1077d04ccbb3Scarlsonj */
1078d04ccbb3Scarlsonj if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
1079e704a8f2Smeem canonize_lif(lif, B_TRUE);
1080d04ccbb3Scarlsonj cancel_lif_timers(lif);
1081d04ccbb3Scarlsonj if (lif->lif_declined != NULL) {
1082d04ccbb3Scarlsonj dlp->dl_smach->dsm_lif_down--;
1083d04ccbb3Scarlsonj lif->lif_declined = NULL;
1084d04ccbb3Scarlsonj }
1085d04ccbb3Scarlsonj dlp->dl_nlifs = 0;
1086d04ccbb3Scarlsonj dlp->dl_lifs = NULL;
1087d04ccbb3Scarlsonj lif->lif_lease = NULL;
1088d04ccbb3Scarlsonj release_lif(lif);
1089d04ccbb3Scarlsonj } else {
1090d04ccbb3Scarlsonj remove_lif(lif);
1091d04ccbb3Scarlsonj }
1092d04ccbb3Scarlsonj }
1093d04ccbb3Scarlsonj
1094d04ccbb3Scarlsonj /*
1095d04ccbb3Scarlsonj * attach_lif(): create a new logical interface, creating the physical
1096d04ccbb3Scarlsonj * interface as necessary.
1097d04ccbb3Scarlsonj *
1098d04ccbb3Scarlsonj * input: const char *: the logical interface name
1099d04ccbb3Scarlsonj * boolean_t: B_TRUE for IPv6
1100d04ccbb3Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails
1101d04ccbb3Scarlsonj * output: dhcp_lif_t *: pointer to new entry, or NULL on failure
1102d04ccbb3Scarlsonj */
1103d04ccbb3Scarlsonj
1104d04ccbb3Scarlsonj dhcp_lif_t *
attach_lif(const char * lname,boolean_t isv6,int * error)1105d04ccbb3Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error)
1106d04ccbb3Scarlsonj {
1107d04ccbb3Scarlsonj dhcp_pif_t *pif;
1108d04ccbb3Scarlsonj char pname[LIFNAMSIZ], *cp;
1109d04ccbb3Scarlsonj
1110d04ccbb3Scarlsonj (void) strlcpy(pname, lname, sizeof (pname));
1111d04ccbb3Scarlsonj if ((cp = strchr(pname, ':')) != NULL)
1112d04ccbb3Scarlsonj *cp = '\0';
1113d04ccbb3Scarlsonj
1114d04ccbb3Scarlsonj if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
1115d04ccbb3Scarlsonj hold_pif(pif);
1116d04ccbb3Scarlsonj else if ((pif = insert_pif(pname, isv6, error)) == NULL)
1117d04ccbb3Scarlsonj return (NULL);
1118d04ccbb3Scarlsonj
1119d04ccbb3Scarlsonj if (lookup_lif_by_name(lname, pif) != NULL) {
1120d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
1121d04ccbb3Scarlsonj lname);
1122d04ccbb3Scarlsonj release_pif(pif);
1123d04ccbb3Scarlsonj *error = DHCP_IPC_E_INVIF;
1124d04ccbb3Scarlsonj return (NULL);
1125d04ccbb3Scarlsonj }
1126d04ccbb3Scarlsonj
1127d04ccbb3Scarlsonj /* If LIF creation fails, then insert_lif discards our PIF hold */
1128d04ccbb3Scarlsonj return (insert_lif(pif, lname, error));
1129d04ccbb3Scarlsonj }
1130d04ccbb3Scarlsonj
1131d04ccbb3Scarlsonj /*
1132d04ccbb3Scarlsonj * set_lif_dhcp(): Set logical interface flags to show that it's managed
1133d04ccbb3Scarlsonj * by DHCP.
1134d04ccbb3Scarlsonj *
1135d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface
1136d04ccbb3Scarlsonj * output: int: set to DHCP_IPC_E_* if operation fails
1137d04ccbb3Scarlsonj */
1138d04ccbb3Scarlsonj
1139d04ccbb3Scarlsonj int
set_lif_dhcp(dhcp_lif_t * lif)1140dc918d99Smeem set_lif_dhcp(dhcp_lif_t *lif)
1141d04ccbb3Scarlsonj {
1142d04ccbb3Scarlsonj int fd;
1143d04ccbb3Scarlsonj int err;
1144d04ccbb3Scarlsonj struct lifreq lifr;
1145e11c3f44Smeem dhcp_pif_t *pif = lif->lif_pif;
1146d04ccbb3Scarlsonj
1147e11c3f44Smeem fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1148d04ccbb3Scarlsonj
1149d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1150d04ccbb3Scarlsonj
1151d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1152d04ccbb3Scarlsonj err = errno;
1153d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
1154d04ccbb3Scarlsonj lif->lif_name);
1155d04ccbb3Scarlsonj return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
1156d04ccbb3Scarlsonj }
1157d04ccbb3Scarlsonj lif->lif_flags = lifr.lifr_flags;
1158d04ccbb3Scarlsonj
1159d04ccbb3Scarlsonj /*
1160d04ccbb3Scarlsonj * Check for conflicting sources of address control, and other
1161d04ccbb3Scarlsonj * unacceptable configurations.
1162d04ccbb3Scarlsonj */
11635c0b7edeSseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
11645c0b7edeSseb IFF_VIRTUAL)) {
1165d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
1166d04ccbb3Scarlsonj lif->lif_name, lifr.lifr_flags);
1167d04ccbb3Scarlsonj return (DHCP_IPC_E_INVIF);
1168d04ccbb3Scarlsonj }
11697c478bd9Sstevel@tonic-gate
11707c478bd9Sstevel@tonic-gate /*
1171dc918d99Smeem * If IFF_DHCPRUNNING is already set on the interface and we're not
1172dc918d99Smeem * adopting it, the agent probably crashed and burned. Note it, but
1173dc918d99Smeem * don't let it stop the proceedings (we're pretty sure we're not
1174dc918d99Smeem * already running, since we were able to bind to our IPC port).
11757c478bd9Sstevel@tonic-gate */
1176d04ccbb3Scarlsonj if (lifr.lifr_flags & IFF_DHCPRUNNING) {
1177dc918d99Smeem dhcpmsg(MSG_VERBOSE, "set_lif_dhcp: IFF_DHCPRUNNING already set"
1178dc918d99Smeem " on %s", lif->lif_name);
11797c478bd9Sstevel@tonic-gate } else {
1180e11c3f44Smeem /*
1181e11c3f44Smeem * If the lif is on an interface under IPMP, IFF_NOFAILOVER
1182e11c3f44Smeem * must be set or the kernel will prevent us from setting
1183e11c3f44Smeem * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to
1184e11c3f44Smeem * migration). We set IFF_DEPRECATED too since the kernel
1185e11c3f44Smeem * will set it automatically when setting IFF_NOFAILOVER,
1186e11c3f44Smeem * causing our lif_flags value to grow stale.
1187e11c3f44Smeem */
1188e11c3f44Smeem if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
1189e11c3f44Smeem lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
1190e11c3f44Smeem
1191d04ccbb3Scarlsonj lifr.lifr_flags |= IFF_DHCPRUNNING;
1192d04ccbb3Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1193d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
1194d04ccbb3Scarlsonj lif->lif_name);
1195d04ccbb3Scarlsonj return (DHCP_IPC_E_INT);
1196d04ccbb3Scarlsonj }
1197d04ccbb3Scarlsonj lif->lif_flags = lifr.lifr_flags;
1198d04ccbb3Scarlsonj }
1199d04ccbb3Scarlsonj return (DHCP_IPC_SUCCESS);
12007c478bd9Sstevel@tonic-gate }
12017c478bd9Sstevel@tonic-gate
12027c478bd9Sstevel@tonic-gate /*
1203d04ccbb3Scarlsonj * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
1204d04ccbb3Scarlsonj * managed by DHCP.
12057c478bd9Sstevel@tonic-gate *
1206d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface
1207d04ccbb3Scarlsonj * output: none
12087c478bd9Sstevel@tonic-gate */
12097c478bd9Sstevel@tonic-gate
12107c478bd9Sstevel@tonic-gate static void
clear_lif_dhcp(dhcp_lif_t * lif)1211d04ccbb3Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif)
12127c478bd9Sstevel@tonic-gate {
1213d04ccbb3Scarlsonj int fd;
1214d04ccbb3Scarlsonj struct lifreq lifr;
12157c478bd9Sstevel@tonic-gate
1216d04ccbb3Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12177c478bd9Sstevel@tonic-gate
1218d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12197c478bd9Sstevel@tonic-gate
1220d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1221d04ccbb3Scarlsonj return;
12227c478bd9Sstevel@tonic-gate
1223d04ccbb3Scarlsonj if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
1224d04ccbb3Scarlsonj return;
12257c478bd9Sstevel@tonic-gate
1226d04ccbb3Scarlsonj lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
1227d04ccbb3Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12287c478bd9Sstevel@tonic-gate }
12297c478bd9Sstevel@tonic-gate
12307c478bd9Sstevel@tonic-gate /*
1231d04ccbb3Scarlsonj * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
1232d04ccbb3Scarlsonj * address will be going away. As the interface is
1233d04ccbb3Scarlsonj * going away, we don't care if there are errors.
12347c478bd9Sstevel@tonic-gate *
1235d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface
1236d04ccbb3Scarlsonj * output: none
12377c478bd9Sstevel@tonic-gate */
12387c478bd9Sstevel@tonic-gate
12397c478bd9Sstevel@tonic-gate void
set_lif_deprecated(dhcp_lif_t * lif)1240d04ccbb3Scarlsonj set_lif_deprecated(dhcp_lif_t *lif)
12417c478bd9Sstevel@tonic-gate {
1242d04ccbb3Scarlsonj int fd;
1243d04ccbb3Scarlsonj struct lifreq lifr;
12447c478bd9Sstevel@tonic-gate
1245d04ccbb3Scarlsonj if (lif->lif_flags & IFF_DEPRECATED)
1246d04ccbb3Scarlsonj return;
1247d04ccbb3Scarlsonj
1248d04ccbb3Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1249d04ccbb3Scarlsonj
1250d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1251d04ccbb3Scarlsonj
1252d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1253d04ccbb3Scarlsonj return;
1254d04ccbb3Scarlsonj
1255d04ccbb3Scarlsonj if (lifr.lifr_flags & IFF_DEPRECATED)
1256d04ccbb3Scarlsonj return;
1257d04ccbb3Scarlsonj
1258d04ccbb3Scarlsonj lifr.lifr_flags |= IFF_DEPRECATED;
1259d04ccbb3Scarlsonj (void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1260d04ccbb3Scarlsonj lif->lif_flags = lifr.lifr_flags;
12617c478bd9Sstevel@tonic-gate }
12627c478bd9Sstevel@tonic-gate
12637c478bd9Sstevel@tonic-gate /*
1264d04ccbb3Scarlsonj * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
1265d04ccbb3Scarlsonj * address will not be going away. This happens if we
1266d04ccbb3Scarlsonj * get a renewal after preferred lifetime but before
1267d04ccbb3Scarlsonj * the valid lifetime.
12687c478bd9Sstevel@tonic-gate *
1269d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface
1270d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success.
12717c478bd9Sstevel@tonic-gate */
12727c478bd9Sstevel@tonic-gate
1273d04ccbb3Scarlsonj boolean_t
clear_lif_deprecated(dhcp_lif_t * lif)1274d04ccbb3Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif)
12757c478bd9Sstevel@tonic-gate {
1276d04ccbb3Scarlsonj int fd;
1277d04ccbb3Scarlsonj struct lifreq lifr;
12787c478bd9Sstevel@tonic-gate
1279d04ccbb3Scarlsonj fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12807c478bd9Sstevel@tonic-gate
1281d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12827c478bd9Sstevel@tonic-gate
1283d04ccbb3Scarlsonj if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1284d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
1285d04ccbb3Scarlsonj lif->lif_name);
12867c478bd9Sstevel@tonic-gate return (B_FALSE);
1287d04ccbb3Scarlsonj }
1288d04ccbb3Scarlsonj
1289d04ccbb3Scarlsonj /*
1290d04ccbb3Scarlsonj * Check for conflicting sources of address control, and other
1291d04ccbb3Scarlsonj * unacceptable configurations.
1292d04ccbb3Scarlsonj */
12935c0b7edeSseb if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
12945c0b7edeSseb IFF_VIRTUAL)) {
1295d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
1296d04ccbb3Scarlsonj "are %llx", lif->lif_name, lifr.lifr_flags);
12977c478bd9Sstevel@tonic-gate return (B_FALSE);
1298d04ccbb3Scarlsonj }
1299d04ccbb3Scarlsonj
1300e11c3f44Smeem /*
1301e11c3f44Smeem * Don't try to clear IFF_DEPRECATED if this is a test address,
1302e11c3f44Smeem * since IPMP's use of IFF_DEPRECATED is not compatible with ours.
1303e11c3f44Smeem */
1304e11c3f44Smeem if (lifr.lifr_flags & IFF_NOFAILOVER)
1305e11c3f44Smeem return (B_TRUE);
1306e11c3f44Smeem
1307d04ccbb3Scarlsonj if (!(lifr.lifr_flags & IFF_DEPRECATED))
1308d04ccbb3Scarlsonj return (B_TRUE);
1309d04ccbb3Scarlsonj
1310d04ccbb3Scarlsonj lifr.lifr_flags &= ~IFF_DEPRECATED;
1311d04ccbb3Scarlsonj if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1312d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
1313d04ccbb3Scarlsonj lif->lif_name);
1314d04ccbb3Scarlsonj return (B_FALSE);
1315d04ccbb3Scarlsonj } else {
1316d04ccbb3Scarlsonj lif->lif_flags = lifr.lifr_flags;
1317d04ccbb3Scarlsonj return (B_TRUE);
1318d04ccbb3Scarlsonj }
1319d04ccbb3Scarlsonj }
1320d04ccbb3Scarlsonj
1321d04ccbb3Scarlsonj /*
1322d04ccbb3Scarlsonj * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
1323d04ccbb3Scarlsonj *
1324d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
1325e704a8f2Smeem * in_addr_t: the address the socket will be bound to (in hbo)
1326e11c3f44Smeem * boolean_t: B_TRUE if the address should be brought up (if needed)
1327d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the socket was opened successfully.
1328d04ccbb3Scarlsonj */
1329d04ccbb3Scarlsonj
1330d04ccbb3Scarlsonj boolean_t
open_ip_lif(dhcp_lif_t * lif,in_addr_t addr_hbo,boolean_t bringup)1331e11c3f44Smeem open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo, boolean_t bringup)
1332d04ccbb3Scarlsonj {
1333e704a8f2Smeem const char *errmsg;
1334e704a8f2Smeem struct lifreq lifr;
1335e704a8f2Smeem int on = 1;
1336a7107231Smeem uchar_t ttl = 255;
1337e11c3f44Smeem uint32_t ifindex;
1338e11c3f44Smeem dhcp_pif_t *pif = lif->lif_pif;
1339e704a8f2Smeem
1340d04ccbb3Scarlsonj if (lif->lif_sock_ip_fd != -1) {
1341d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
1342d04ccbb3Scarlsonj lif->lif_name);
1343d04ccbb3Scarlsonj return (B_FALSE);
1344d04ccbb3Scarlsonj }
1345d04ccbb3Scarlsonj
1346d04ccbb3Scarlsonj lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
1347d04ccbb3Scarlsonj if (lif->lif_sock_ip_fd == -1) {
1348e704a8f2Smeem errmsg = "cannot create v4 socket";
1349e704a8f2Smeem goto failure;
1350d04ccbb3Scarlsonj }
1351d04ccbb3Scarlsonj
1352e704a8f2Smeem if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) {
1353e704a8f2Smeem errmsg = "cannot bind v4 socket";
1354e704a8f2Smeem goto failure;
1355d04ccbb3Scarlsonj }
1356d04ccbb3Scarlsonj
1357e704a8f2Smeem /*
1358e704a8f2Smeem * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
1359e704a8f2Smeem * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
1360e704a8f2Smeem * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that
1361e704a8f2Smeem * the IP module will accept unicast DHCP traffic regardless of the IP
1362e704a8f2Smeem * address it's sent to. (We'll then figure out which packets are
1363e704a8f2Smeem * ours based on the xid.)
1364e704a8f2Smeem */
1365e704a8f2Smeem if (addr_hbo == INADDR_ANY) {
1366e704a8f2Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC,
1367e704a8f2Smeem &on, sizeof (int)) == -1) {
1368e704a8f2Smeem errmsg = "cannot set IP_UNSPEC_SRC";
1369e704a8f2Smeem goto failure;
1370e704a8f2Smeem }
1371e704a8f2Smeem
1372e704a8f2Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
1373e11c3f44Smeem &pif->pif_index, sizeof (int)) == -1) {
1374e704a8f2Smeem errmsg = "cannot set IP_DHCPINIT_IF";
1375e704a8f2Smeem goto failure;
1376e704a8f2Smeem }
1377e704a8f2Smeem }
1378e704a8f2Smeem
1379a7107231Smeem /*
1380a7107231Smeem * Unfortunately, some hardware (such as the Linksys WRT54GC)
1381a7107231Smeem * decrements the TTL *prior* to accepting DHCP traffic destined
1382a7107231Smeem * for it. To workaround this, tell IP to use a TTL of 255 for
1383a7107231Smeem * broadcast packets sent from this socket.
1384a7107231Smeem */
1385a7107231Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl,
1386a7107231Smeem sizeof (uchar_t)) == -1) {
1387a7107231Smeem errmsg = "cannot set IP_BROADCAST_TTL";
1388a7107231Smeem goto failure;
1389a7107231Smeem }
1390a7107231Smeem
1391e11c3f44Smeem ifindex = pif->pif_under_ipmp ? pif->pif_grindex : pif->pif_index;
1392e11c3f44Smeem if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, &ifindex,
1393e11c3f44Smeem sizeof (int)) == -1) {
1394e704a8f2Smeem errmsg = "cannot set IP_BOUND_IF";
1395e704a8f2Smeem goto failure;
1396e704a8f2Smeem }
1397e704a8f2Smeem
1398e704a8f2Smeem (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1399e704a8f2Smeem if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1400e704a8f2Smeem errmsg = "cannot get interface flags";
1401e704a8f2Smeem goto failure;
1402e704a8f2Smeem }
1403e704a8f2Smeem
1404e11c3f44Smeem /*
1405e11c3f44Smeem * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must
1406e11c3f44Smeem * be set or the kernel will prevent us from setting IFF_DHCPRUNNING
1407e11c3f44Smeem * (since the subsequent IFF_UP would lead to migration). We set
1408e11c3f44Smeem * IFF_DEPRECATED too since the kernel will set it automatically when
1409e11c3f44Smeem * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale.
1410e11c3f44Smeem */
1411e11c3f44Smeem if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) {
1412e11c3f44Smeem lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
1413e11c3f44Smeem if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1414e11c3f44Smeem errmsg = "cannot set IFF_NOFAILOVER";
1415e11c3f44Smeem goto failure;
1416e11c3f44Smeem }
1417e11c3f44Smeem }
1418e11c3f44Smeem lif->lif_flags = lifr.lifr_flags;
1419e11c3f44Smeem
1420e11c3f44Smeem /*
1421e11c3f44Smeem * If this is initial bringup, make sure the address we're acquiring a
1422e11c3f44Smeem * lease on is IFF_UP.
1423e11c3f44Smeem */
1424e11c3f44Smeem if (bringup && !(lifr.lifr_flags & IFF_UP)) {
1425e704a8f2Smeem /*
1426e704a8f2Smeem * Start from a clean slate.
1427e704a8f2Smeem */
1428e704a8f2Smeem canonize_lif(lif, B_FALSE);
1429e704a8f2Smeem
1430e704a8f2Smeem lifr.lifr_flags |= IFF_UP;
1431e704a8f2Smeem if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1432e704a8f2Smeem errmsg = "cannot bring up";
1433e704a8f2Smeem goto failure;
1434e704a8f2Smeem }
143510e672b7Smeem lif->lif_flags = lifr.lifr_flags;
1436e704a8f2Smeem
1437e704a8f2Smeem /*
1438e704a8f2Smeem * When bringing 0.0.0.0 IFF_UP, the kernel changes the
1439e704a8f2Smeem * netmask to 255.0.0.0, so re-fetch our expected netmask.
1440e704a8f2Smeem */
1441e704a8f2Smeem if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) {
1442e704a8f2Smeem errmsg = "cannot get netmask";
1443e704a8f2Smeem goto failure;
1444e704a8f2Smeem }
1445e704a8f2Smeem
1446e704a8f2Smeem lif->lif_netmask =
1447e704a8f2Smeem ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
1448e704a8f2Smeem }
1449e704a8f2Smeem
1450e11c3f44Smeem /*
1451e11c3f44Smeem * Usually, bringing up the address we're acquiring a lease on is
1452e11c3f44Smeem * sufficient to allow packets to be sent and received via the
1453e11c3f44Smeem * IP_BOUND_IF we did earlier. However, if we're acquiring a lease on
1454e11c3f44Smeem * an underlying IPMP interface, the group interface will be used for
1455e11c3f44Smeem * sending and receiving IP packets via IP_BOUND_IF. Thus, ensure at
1456e11c3f44Smeem * least one address on the group interface is IFF_UP.
1457e11c3f44Smeem */
1458e11c3f44Smeem if (bringup && pif->pif_under_ipmp) {
1459e11c3f44Smeem (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
1460e11c3f44Smeem if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1461e11c3f44Smeem errmsg = "cannot get IPMP group interface flags";
1462e11c3f44Smeem goto failure;
1463e11c3f44Smeem }
1464e11c3f44Smeem
1465e11c3f44Smeem if (!(lifr.lifr_flags & IFF_UP)) {
1466e11c3f44Smeem lifr.lifr_flags |= IFF_UP;
1467e11c3f44Smeem if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1468e11c3f44Smeem errmsg = "cannot bring up IPMP group interface";
1469e11c3f44Smeem goto failure;
1470e11c3f44Smeem }
1471e11c3f44Smeem }
1472e11c3f44Smeem }
1473e11c3f44Smeem
1474e704a8f2Smeem lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
1475e704a8f2Smeem dhcp_packet_lif, lif);
1476e704a8f2Smeem if (lif->lif_packet_id == -1) {
1477e704a8f2Smeem errmsg = "cannot register to receive DHCP packets";
1478e704a8f2Smeem goto failure;
1479e704a8f2Smeem }
1480e704a8f2Smeem
1481e704a8f2Smeem return (B_TRUE);
1482e704a8f2Smeem failure:
1483e704a8f2Smeem dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg);
1484d04ccbb3Scarlsonj close_ip_lif(lif);
1485d04ccbb3Scarlsonj return (B_FALSE);
14867c478bd9Sstevel@tonic-gate }
14877c478bd9Sstevel@tonic-gate
14887c478bd9Sstevel@tonic-gate /*
1489d04ccbb3Scarlsonj * close_ip_lif(): close an IP socket for I/O on a given LIF.
14907c478bd9Sstevel@tonic-gate *
1491d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
1492d04ccbb3Scarlsonj * output: none
14937c478bd9Sstevel@tonic-gate */
14947c478bd9Sstevel@tonic-gate
14957c478bd9Sstevel@tonic-gate void
close_ip_lif(dhcp_lif_t * lif)1496d04ccbb3Scarlsonj close_ip_lif(dhcp_lif_t *lif)
14977c478bd9Sstevel@tonic-gate {
1498e704a8f2Smeem if (lif->lif_packet_id != -1) {
1499e704a8f2Smeem (void) iu_unregister_event(eh, lif->lif_packet_id, NULL);
1500e704a8f2Smeem lif->lif_packet_id = -1;
15017c478bd9Sstevel@tonic-gate }
1502d04ccbb3Scarlsonj if (lif->lif_sock_ip_fd != -1) {
1503d04ccbb3Scarlsonj (void) close(lif->lif_sock_ip_fd);
1504d04ccbb3Scarlsonj lif->lif_sock_ip_fd = -1;
15057c478bd9Sstevel@tonic-gate }
15067c478bd9Sstevel@tonic-gate }
15077c478bd9Sstevel@tonic-gate
15087c478bd9Sstevel@tonic-gate /*
1509d04ccbb3Scarlsonj * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
1510d04ccbb3Scarlsonj * address or some other conflict. This is used in
1511d04ccbb3Scarlsonj * send_declines() to report failure back to the server.
15127c478bd9Sstevel@tonic-gate *
1513d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
1514d04ccbb3Scarlsonj * const char *: text string explaining why the address is declined
1515d04ccbb3Scarlsonj * output: none
15167c478bd9Sstevel@tonic-gate */
15177c478bd9Sstevel@tonic-gate
15187c478bd9Sstevel@tonic-gate void
lif_mark_decline(dhcp_lif_t * lif,const char * reason)1519d04ccbb3Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason)
15207c478bd9Sstevel@tonic-gate {
1521d04ccbb3Scarlsonj if (lif->lif_declined == NULL) {
1522d04ccbb3Scarlsonj dhcp_lease_t *dlp;
15237c478bd9Sstevel@tonic-gate
1524d04ccbb3Scarlsonj lif->lif_declined = reason;
1525d04ccbb3Scarlsonj if ((dlp = lif->lif_lease) != NULL)
1526d04ccbb3Scarlsonj dlp->dl_smach->dsm_lif_down++;
1527d04ccbb3Scarlsonj }
1528d04ccbb3Scarlsonj }
15297c478bd9Sstevel@tonic-gate
15307c478bd9Sstevel@tonic-gate /*
1531d04ccbb3Scarlsonj * schedule_lif_timer(): schedules the LIF-related timer
1532d04ccbb3Scarlsonj *
1533d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
1534d04ccbb3Scarlsonj * dhcp_timer_t *: the timer to schedule
1535d04ccbb3Scarlsonj * iu_tq_callback_t *: the callback to call upon firing
1536d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully
15377c478bd9Sstevel@tonic-gate */
15387c478bd9Sstevel@tonic-gate
1539d04ccbb3Scarlsonj boolean_t
schedule_lif_timer(dhcp_lif_t * lif,dhcp_timer_t * dt,iu_tq_callback_t * expire)1540d04ccbb3Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
15417c478bd9Sstevel@tonic-gate {
1542d04ccbb3Scarlsonj /*
1543d04ccbb3Scarlsonj * If there's a timer running, cancel it and release its lease
1544d04ccbb3Scarlsonj * reference.
1545d04ccbb3Scarlsonj */
1546d04ccbb3Scarlsonj if (dt->dt_id != -1) {
1547d04ccbb3Scarlsonj if (!cancel_timer(dt))
1548d04ccbb3Scarlsonj return (B_FALSE);
1549d04ccbb3Scarlsonj release_lif(lif);
1550d04ccbb3Scarlsonj }
1551d04ccbb3Scarlsonj
1552d04ccbb3Scarlsonj if (schedule_timer(dt, expire, lif)) {
1553d04ccbb3Scarlsonj hold_lif(lif);
1554d04ccbb3Scarlsonj return (B_TRUE);
1555d04ccbb3Scarlsonj } else {
1556d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
1557d04ccbb3Scarlsonj "schedule_lif_timer: cannot schedule timer");
1558d04ccbb3Scarlsonj return (B_FALSE);
1559d04ccbb3Scarlsonj }
15607c478bd9Sstevel@tonic-gate }
15617c478bd9Sstevel@tonic-gate
15627c478bd9Sstevel@tonic-gate /*
1563d04ccbb3Scarlsonj * cancel_lif_timer(): cancels a LIF-related timer
15647c478bd9Sstevel@tonic-gate *
1565d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
1566d04ccbb3Scarlsonj * dhcp_timer_t *: the timer to cancel
1567d04ccbb3Scarlsonj * output: none
15687c478bd9Sstevel@tonic-gate */
15697c478bd9Sstevel@tonic-gate
15707c478bd9Sstevel@tonic-gate static void
cancel_lif_timer(dhcp_lif_t * lif,dhcp_timer_t * dt)1571d04ccbb3Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
15727c478bd9Sstevel@tonic-gate {
1573d04ccbb3Scarlsonj if (dt->dt_id == -1)
1574d04ccbb3Scarlsonj return;
1575d04ccbb3Scarlsonj if (cancel_timer(dt)) {
1576d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG2,
1577d04ccbb3Scarlsonj "cancel_lif_timer: canceled expiry timer on %s",
1578d04ccbb3Scarlsonj lif->lif_name);
1579d04ccbb3Scarlsonj release_lif(lif);
1580d04ccbb3Scarlsonj } else {
1581d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
1582d04ccbb3Scarlsonj "cancel_lif_timer: cannot cancel timer on %s",
1583d04ccbb3Scarlsonj lif->lif_name);
15847c478bd9Sstevel@tonic-gate }
15857c478bd9Sstevel@tonic-gate }
15867c478bd9Sstevel@tonic-gate
15877c478bd9Sstevel@tonic-gate /*
1588d04ccbb3Scarlsonj * cancel_lif_timers(): cancels the LIF-related timers
15897c478bd9Sstevel@tonic-gate *
1590d04ccbb3Scarlsonj * input: dhcp_lif_t *: the logical interface to operate on
1591d04ccbb3Scarlsonj * output: none
15927c478bd9Sstevel@tonic-gate */
15937c478bd9Sstevel@tonic-gate
15947c478bd9Sstevel@tonic-gate void
cancel_lif_timers(dhcp_lif_t * lif)1595d04ccbb3Scarlsonj cancel_lif_timers(dhcp_lif_t *lif)
15967c478bd9Sstevel@tonic-gate {
1597d04ccbb3Scarlsonj cancel_lif_timer(lif, &lif->lif_preferred);
1598d04ccbb3Scarlsonj cancel_lif_timer(lif, &lif->lif_expire);
15997c478bd9Sstevel@tonic-gate }
16007c478bd9Sstevel@tonic-gate
16017c478bd9Sstevel@tonic-gate /*
1602d04ccbb3Scarlsonj * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
1603d04ccbb3Scarlsonj * file descriptors (v4_sock_fd and v6_sock_fd).
16047c478bd9Sstevel@tonic-gate *
1605d04ccbb3Scarlsonj * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
1606d04ccbb3Scarlsonj * output: none
16077c478bd9Sstevel@tonic-gate */
16087c478bd9Sstevel@tonic-gate
1609d04ccbb3Scarlsonj uint_t
get_max_mtu(boolean_t isv6)1610d04ccbb3Scarlsonj get_max_mtu(boolean_t isv6)
16117c478bd9Sstevel@tonic-gate {
1612d04ccbb3Scarlsonj uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
16137c478bd9Sstevel@tonic-gate
1614d04ccbb3Scarlsonj if (*mtup == 0) {
1615d04ccbb3Scarlsonj dhcp_pif_t *pif;
1616d04ccbb3Scarlsonj dhcp_lif_t *lif;
1617d04ccbb3Scarlsonj struct lifreq lifr;
1618d04ccbb3Scarlsonj
1619d04ccbb3Scarlsonj /* Set an arbitrary lower bound */
1620d04ccbb3Scarlsonj *mtup = 1024;
1621d04ccbb3Scarlsonj pif = isv6 ? v6root : v4root;
1622d04ccbb3Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
1623d04ccbb3Scarlsonj for (lif = pif->pif_lifs; lif != NULL;
1624d04ccbb3Scarlsonj lif = lif->lif_next) {
1625d04ccbb3Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name,
1626d04ccbb3Scarlsonj LIFNAMSIZ);
1627d04ccbb3Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
1628d04ccbb3Scarlsonj -1 && lifr.lifr_mtu > *mtup) {
1629d04ccbb3Scarlsonj *mtup = lifr.lifr_mtu;
16307c478bd9Sstevel@tonic-gate }
1631d04ccbb3Scarlsonj }
1632d04ccbb3Scarlsonj }
1633d04ccbb3Scarlsonj }
1634d04ccbb3Scarlsonj return (*mtup);
16357c478bd9Sstevel@tonic-gate }
16367c478bd9Sstevel@tonic-gate
16377c478bd9Sstevel@tonic-gate /*
1638d04ccbb3Scarlsonj * expired_lif_state(): summarize the state of expired LIFs on a given state
1639d04ccbb3Scarlsonj * machine.
16407c478bd9Sstevel@tonic-gate *
1641d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to scan
1642d04ccbb3Scarlsonj * output: dhcp_expire_t: overall state
16437c478bd9Sstevel@tonic-gate */
16447c478bd9Sstevel@tonic-gate
1645d04ccbb3Scarlsonj dhcp_expire_t
expired_lif_state(dhcp_smach_t * dsmp)1646d04ccbb3Scarlsonj expired_lif_state(dhcp_smach_t *dsmp)
16477c478bd9Sstevel@tonic-gate {
1648d04ccbb3Scarlsonj dhcp_lease_t *dlp;
1649d04ccbb3Scarlsonj dhcp_lif_t *lif;
1650d04ccbb3Scarlsonj uint_t nlifs;
1651d04ccbb3Scarlsonj uint_t numlifs;
1652d04ccbb3Scarlsonj uint_t numexp;
16537c478bd9Sstevel@tonic-gate
1654d04ccbb3Scarlsonj numlifs = numexp = 0;
1655d04ccbb3Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1656d04ccbb3Scarlsonj lif = dlp->dl_lifs;
1657d04ccbb3Scarlsonj nlifs = dlp->dl_nlifs;
1658d04ccbb3Scarlsonj numlifs += nlifs;
1659d04ccbb3Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1660d04ccbb3Scarlsonj if (lif->lif_expired)
1661d04ccbb3Scarlsonj numexp++;
1662d04ccbb3Scarlsonj }
1663d04ccbb3Scarlsonj }
1664d04ccbb3Scarlsonj if (numlifs == 0)
1665d04ccbb3Scarlsonj return (DHCP_EXP_NOLIFS);
1666d04ccbb3Scarlsonj else if (numexp == 0)
1667d04ccbb3Scarlsonj return (DHCP_EXP_NOEXP);
1668d04ccbb3Scarlsonj else if (numlifs == numexp)
1669d04ccbb3Scarlsonj return (DHCP_EXP_ALLEXP);
1670d04ccbb3Scarlsonj else
1671d04ccbb3Scarlsonj return (DHCP_EXP_SOMEEXP);
16727c478bd9Sstevel@tonic-gate }
16737c478bd9Sstevel@tonic-gate
16747c478bd9Sstevel@tonic-gate /*
1675d04ccbb3Scarlsonj * find_expired_lif(): find the first expired LIF on a given state machine
1676d04ccbb3Scarlsonj *
1677d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to scan
1678d04ccbb3Scarlsonj * output: dhcp_lif_t *: the first expired LIF, or NULL if none.
16797c478bd9Sstevel@tonic-gate */
16807c478bd9Sstevel@tonic-gate
1681d04ccbb3Scarlsonj dhcp_lif_t *
find_expired_lif(dhcp_smach_t * dsmp)1682d04ccbb3Scarlsonj find_expired_lif(dhcp_smach_t *dsmp)
1683d04ccbb3Scarlsonj {
1684d04ccbb3Scarlsonj dhcp_lease_t *dlp;
1685d04ccbb3Scarlsonj dhcp_lif_t *lif;
1686d04ccbb3Scarlsonj uint_t nlifs;
1687d04ccbb3Scarlsonj
1688d04ccbb3Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1689d04ccbb3Scarlsonj lif = dlp->dl_lifs;
1690d04ccbb3Scarlsonj nlifs = dlp->dl_nlifs;
1691d04ccbb3Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1692d04ccbb3Scarlsonj if (lif->lif_expired)
1693d04ccbb3Scarlsonj return (lif);
1694d04ccbb3Scarlsonj }
1695d04ccbb3Scarlsonj }
1696d04ccbb3Scarlsonj return (NULL);
1697d04ccbb3Scarlsonj }
1698d04ccbb3Scarlsonj
1699d04ccbb3Scarlsonj /*
1700d04ccbb3Scarlsonj * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used
1701d04ccbb3Scarlsonj * only for DHCPv6.
1702d04ccbb3Scarlsonj *
1703d04ccbb3Scarlsonj * input: none
1704d04ccbb3Scarlsonj * output: none
1705d04ccbb3Scarlsonj */
1706d04ccbb3Scarlsonj
1707d04ccbb3Scarlsonj void
remove_v6_strays(void)1708d04ccbb3Scarlsonj remove_v6_strays(void)
1709d04ccbb3Scarlsonj {
1710d04ccbb3Scarlsonj struct lifnum lifn;
1711d04ccbb3Scarlsonj struct lifconf lifc;
1712d04ccbb3Scarlsonj struct lifreq *lifrp, *lifrmax;
1713d04ccbb3Scarlsonj uint_t numifs;
1714d04ccbb3Scarlsonj uint64_t flags;
1715d04ccbb3Scarlsonj
1716d04ccbb3Scarlsonj /*
1717d04ccbb3Scarlsonj * Get the approximate number of interfaces in the system. It's only
1718d04ccbb3Scarlsonj * approximate because the system is dynamic -- interfaces may be
1719d04ccbb3Scarlsonj * plumbed or unplumbed at any time. This is also the reason for the
1720d04ccbb3Scarlsonj * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
1721d04ccbb3Scarlsonj */
1722d04ccbb3Scarlsonj (void) memset(&lifn, 0, sizeof (lifn));
1723d04ccbb3Scarlsonj lifn.lifn_family = AF_INET6;
1724d04ccbb3Scarlsonj lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1725d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
1726d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1727d04ccbb3Scarlsonj "remove_v6_strays: cannot read number of interfaces");
1728d04ccbb3Scarlsonj numifs = 10;
1729d04ccbb3Scarlsonj } else {
1730d04ccbb3Scarlsonj numifs = lifn.lifn_count + 10;
1731d04ccbb3Scarlsonj }
1732d04ccbb3Scarlsonj
1733d04ccbb3Scarlsonj /*
1734d04ccbb3Scarlsonj * Get the interface information. We do this in a loop so that we can
1735d04ccbb3Scarlsonj * recover from EINVAL from the kernel -- delivered when the buffer is
1736d04ccbb3Scarlsonj * too small.
1737d04ccbb3Scarlsonj */
1738d04ccbb3Scarlsonj (void) memset(&lifc, 0, sizeof (lifc));
1739d04ccbb3Scarlsonj lifc.lifc_family = AF_INET6;
1740d04ccbb3Scarlsonj lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1741d04ccbb3Scarlsonj for (;;) {
1742d04ccbb3Scarlsonj lifc.lifc_len = numifs * sizeof (*lifrp);
1743d04ccbb3Scarlsonj lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
1744d04ccbb3Scarlsonj if (lifrp == NULL) {
1745d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1746d04ccbb3Scarlsonj "remove_v6_strays: cannot allocate memory");
1747d04ccbb3Scarlsonj free(lifc.lifc_buf);
1748d04ccbb3Scarlsonj return;
1749d04ccbb3Scarlsonj }
1750d04ccbb3Scarlsonj lifc.lifc_buf = (caddr_t)lifrp;
1751d04ccbb3Scarlsonj errno = 0;
1752d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
1753d04ccbb3Scarlsonj lifc.lifc_len < numifs * sizeof (*lifrp))
17547c478bd9Sstevel@tonic-gate break;
1755d04ccbb3Scarlsonj if (errno == 0 || errno == EINVAL) {
1756d04ccbb3Scarlsonj numifs <<= 1;
1757d04ccbb3Scarlsonj } else {
1758d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
1759d04ccbb3Scarlsonj free(lifc.lifc_buf);
1760d04ccbb3Scarlsonj return;
17617c478bd9Sstevel@tonic-gate }
17627c478bd9Sstevel@tonic-gate }
17637c478bd9Sstevel@tonic-gate
1764d04ccbb3Scarlsonj lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
1765d04ccbb3Scarlsonj for (; lifrp < lifrmax; lifrp++) {
17667c478bd9Sstevel@tonic-gate /*
1767d04ccbb3Scarlsonj * Get the interface flags; we're interested in the DHCP ones.
17687c478bd9Sstevel@tonic-gate */
1769d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
1770d04ccbb3Scarlsonj continue;
1771d04ccbb3Scarlsonj flags = lifrp->lifr_flags;
1772d04ccbb3Scarlsonj if (!(flags & IFF_DHCPRUNNING))
1773d04ccbb3Scarlsonj continue;
17747c478bd9Sstevel@tonic-gate /*
1775d04ccbb3Scarlsonj * If the interface has a link-local address, then we don't
1776d04ccbb3Scarlsonj * control it. Just remove the flag.
17777c478bd9Sstevel@tonic-gate */
1778d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
1779d04ccbb3Scarlsonj continue;
1780d04ccbb3Scarlsonj if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
1781d04ccbb3Scarlsonj lifr_addr)->sin6_addr)) {
1782d04ccbb3Scarlsonj lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
1783d04ccbb3Scarlsonj (void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
1784d04ccbb3Scarlsonj continue;
1785d04ccbb3Scarlsonj }
17867c478bd9Sstevel@tonic-gate /*
1787d04ccbb3Scarlsonj * All others are (or were) under our control. Clean up by
1788d04ccbb3Scarlsonj * removing them.
17897c478bd9Sstevel@tonic-gate */
1790d04ccbb3Scarlsonj if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
1791d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
1792d04ccbb3Scarlsonj lifrp->lifr_name);
1793d04ccbb3Scarlsonj } else if (errno != ENXIO) {
1794d04ccbb3Scarlsonj dhcpmsg(MSG_ERR,
1795d04ccbb3Scarlsonj "remove_v6_strays: SIOCLIFREMOVEIF %s",
1796d04ccbb3Scarlsonj lifrp->lifr_name);
17977c478bd9Sstevel@tonic-gate }
17987c478bd9Sstevel@tonic-gate }
1799d04ccbb3Scarlsonj free(lifc.lifc_buf);
18007c478bd9Sstevel@tonic-gate }
1801