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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * ADOPTING state of the client state machine. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/types.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <stdlib.h> 34 #include <signal.h> 35 #include <sys/sockio.h> 36 #include <sys/socket.h> 37 #include <netinet/in.h> 38 #include <sys/systeminfo.h> 39 #include <netinet/inetutil.h> 40 #include <netinet/dhcp.h> 41 #include <dhcpmsg.h> 42 43 #include "async.h" 44 #include "util.h" 45 #include "packet.h" 46 #include "interface.h" 47 #include "states.h" 48 49 50 typedef struct { 51 char dk_if_name[IFNAMSIZ]; 52 char dk_ack[1]; 53 } dhcp_kcache_t; 54 55 static int get_dhcp_kcache(dhcp_kcache_t **, size_t *); 56 57 /* 58 * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot 59 * 60 * input: void 61 * output: int: nonzero on success, zero on failure 62 */ 63 64 int 65 dhcp_adopt(void) 66 { 67 int retval; 68 dhcp_kcache_t *kcache = NULL; 69 size_t kcache_size; 70 PKT_LIST *plp = NULL; 71 struct ifslist *ifsp; 72 73 retval = get_dhcp_kcache(&kcache, &kcache_size); 74 if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) { 75 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache"); 76 goto failure; 77 } 78 79 dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name); 80 81 /* 82 * convert the kernel's ACK into binary 83 */ 84 85 plp = calloc(1, sizeof (PKT_LIST)); 86 if (plp == NULL) 87 goto failure; 88 89 plp->len = strlen(kcache->dk_ack) / 2; 90 plp->pkt = malloc(plp->len); 91 if (plp->pkt == NULL) 92 goto failure; 93 94 dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len); 95 96 if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, &plp->len) 97 != 0) { 98 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK"); 99 goto failure; 100 } 101 102 if (dhcp_options_scan(plp, B_TRUE) != 0) { 103 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK"); 104 goto failure; 105 } 106 107 /* 108 * make an interface to represent the "cached interface" in 109 * the kernel, hook up the ACK packet we made, and send out 110 * the extend request (to attempt to renew the lease). 111 * 112 * we do a send_extend() instead of doing a dhcp_init_reboot() 113 * because although dhcp_init_reboot() is more correct from a 114 * protocol perspective, it introduces a window where a 115 * diskless client has no IP address but may need to page in 116 * more of this program. we could mlockall(), but that's 117 * going to be a mess, especially with handling malloc() and 118 * stack growth, so it's easier to just renew(). the only 119 * catch here is that if we are not granted a renewal, we're 120 * totally hosed and can only bail out. 121 */ 122 123 ifsp = insert_ifs(kcache->dk_if_name, B_TRUE, &retval); 124 if (ifsp == NULL) 125 goto failure; 126 127 ifsp->if_state = ADOPTING; 128 ifsp->if_dflags |= DHCP_IF_PRIMARY; 129 130 /* 131 * move to BOUND and use the information in our ACK packet. 132 * adoption will continue after DAD via dhcp_adopt_complete. 133 */ 134 135 if (dhcp_bound(ifsp, plp) == 0) { 136 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet"); 137 goto failure; 138 } 139 140 free(kcache); 141 return (1); 142 143 failure: 144 free(kcache); 145 if (plp != NULL) 146 free(plp->pkt); 147 free(plp); 148 return (0); 149 } 150 151 /* 152 * dhcp_adopt_complete(): completes interface adoption process after kernel 153 * duplicate address detection (DAD) is done. 154 * 155 * input: struct ifslist *: the interface on which a lease is being adopted 156 * output: none 157 */ 158 159 void 160 dhcp_adopt_complete(struct ifslist *ifsp) 161 { 162 dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption"); 163 164 if (async_start(ifsp, DHCP_EXTEND, B_FALSE) == 0) { 165 dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed"); 166 return; 167 } 168 169 if (dhcp_extending(ifsp) == 0) { 170 dhcpmsg(MSG_CRIT, 171 "dhcp_adopt_complete: cannot send renew request"); 172 return; 173 } 174 175 if (grandparent != (pid_t)0) { 176 dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%i)" 177 " to exit.", grandparent); 178 (void) kill(grandparent, SIGALRM); 179 } 180 } 181 182 /* 183 * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel 184 * 185 * input: dhcp_kcache_t **: a dynamically-allocated cache packet 186 * size_t *: the length of that packet (on return) 187 * output: int: nonzero on success, zero on failure 188 */ 189 190 static int 191 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size) 192 { 193 char dummy; 194 long size; 195 196 size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy)); 197 if (size == -1) 198 return (0); 199 200 *kcache_size = size; 201 *kernel_cachep = malloc(*kcache_size); 202 if (*kernel_cachep == NULL) 203 return (0); 204 205 (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size); 206 return (1); 207 } 208