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