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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * DECLINE/RELEASE configuration functionality for the DHCP client. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/types.h> 31 #include <unistd.h> 32 #include <string.h> 33 #include <netinet/in.h> 34 #include <net/if.h> 35 #include <netinet/dhcp.h> 36 #include <netinet/dhcp6.h> 37 #include <dhcpmsg.h> 38 #include <dhcp_hostconf.h> 39 40 #include "agent.h" 41 #include "packet.h" 42 #include "interface.h" 43 #include "states.h" 44 45 static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int); 46 47 /* 48 * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the 49 * server to indicate a problem with the offered addresses. 50 * The failing addresses are removed from the leases. 51 * 52 * input: dhcp_smach_t *: the state machine sending DECLINE 53 * output: void 54 */ 55 56 void 57 send_declines(dhcp_smach_t *dsmp) 58 { 59 dhcp_pkt_t *dpkt; 60 dhcp_lease_t *dlp, *dlpn; 61 uint_t nlifs; 62 dhcp_lif_t *lif, *lifn; 63 boolean_t got_one; 64 65 /* 66 * Create an empty DECLINE message. We'll stuff the information into 67 * this message as we find it. 68 */ 69 if (dsmp->dsm_isv6) { 70 if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL) 71 return; 72 (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 73 dsmp->dsm_serverid, dsmp->dsm_serveridlen); 74 } else { 75 ipaddr_t serverip; 76 77 /* 78 * If this ack is from BOOTP, then there's no way to send a 79 * decline. Note that since we haven't bound yet, we can't 80 * just check the BOOTP flag. 81 */ 82 if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL) 83 return; 84 85 if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL) 86 return; 87 IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 88 (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 89 } 90 91 /* 92 * Loop over the leases, looking for ones with now-broken LIFs. Add 93 * each one found to the DECLINE message, and remove it from the list. 94 * Also remove any completely declined leases. 95 */ 96 got_one = B_FALSE; 97 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 98 dlpn = dlp->dl_next; 99 lif = dlp->dl_lifs; 100 for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) { 101 lifn = lif->lif_next; 102 if (lif->lif_declined != NULL) { 103 (void) add_pkt_lif(dpkt, lif, 104 DHCPV6_STAT_UNSPECFAIL, lif->lif_declined); 105 unplumb_lif(lif); 106 got_one = B_TRUE; 107 } 108 } 109 if (dlp->dl_nlifs == 0) 110 remove_lease(dlp); 111 } 112 113 if (!got_one) 114 return; 115 116 (void) set_smach_state(dsmp, DECLINING); 117 118 if (dsmp->dsm_isv6) { 119 (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 120 stop_release_decline, DHCPV6_DEC_TIMEOUT, 0); 121 } else { 122 (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 123 124 (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL); 125 } 126 } 127 128 /* 129 * dhcp_release(): sends a RELEASE message to a DHCP server and removes 130 * the all interfaces for the given state machine from DHCP 131 * control. Called back by script handler. 132 * 133 * input: dhcp_smach_t *: the state machine to send the RELEASE on and remove 134 * void *: an optional text explanation to send with the message 135 * output: int: 1 on success, 0 on failure 136 */ 137 138 int 139 dhcp_release(dhcp_smach_t *dsmp, void *arg) 140 { 141 const char *msg = arg; 142 dhcp_pkt_t *dpkt; 143 dhcp_lease_t *dlp; 144 dhcp_lif_t *lif; 145 ipaddr_t serverip; 146 uint_t nlifs; 147 148 if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) || 149 !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) { 150 ipc_action_finish(dsmp, DHCP_IPC_E_INT); 151 return (0); 152 } 153 154 dhcpmsg(MSG_INFO, "releasing leases for state machine %s", 155 dsmp->dsm_name); 156 (void) set_smach_state(dsmp, RELEASING); 157 158 if (dsmp->dsm_isv6) { 159 dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE); 160 (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 161 dsmp->dsm_serverid, dsmp->dsm_serveridlen); 162 163 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 164 lif = dlp->dl_lifs; 165 for (nlifs = dlp->dl_nlifs; nlifs > 0; 166 nlifs--, lif = lif->lif_next) { 167 (void) add_pkt_lif(dpkt, lif, 168 DHCPV6_STAT_SUCCESS, NULL); 169 } 170 } 171 172 /* 173 * Must kill off the leases before attempting to tell the 174 * server. 175 */ 176 deprecate_leases(dsmp); 177 178 /* 179 * For DHCPv6, this is a transaction, rather than just a 180 * one-shot message. When this transaction is done, we'll 181 * finish the invoking async operation. 182 */ 183 (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 184 stop_release_decline, DHCPV6_REL_TIMEOUT, 0); 185 } else { 186 if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) { 187 dpkt = init_pkt(dsmp, RELEASE); 188 if (msg != NULL) { 189 (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 190 strlen(msg) + 1); 191 } 192 lif = dlp->dl_lifs; 193 (void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL); 194 195 IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 196 (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 197 (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 198 (void) send_pkt(dsmp, dpkt, serverip, NULL); 199 } 200 201 /* 202 * XXX this totally sucks, but since udp is best-effort, 203 * without this delay, there's a good chance that the packet 204 * that we just enqueued for sending will get pitched 205 * when we canonize the interface through remove_smach. 206 */ 207 208 (void) usleep(500); 209 deprecate_leases(dsmp); 210 211 finished_smach(dsmp, DHCP_IPC_SUCCESS); 212 } 213 return (1); 214 } 215 216 /* 217 * dhcp_drop(): drops the interface from DHCP control; callback from script 218 * handler 219 * 220 * input: dhcp_smach_t *: the state machine dropping leases 221 * void *: unused 222 * output: int: always 1 223 */ 224 225 /* ARGSUSED1 */ 226 int 227 dhcp_drop(dhcp_smach_t *dsmp, void *arg) 228 { 229 dhcpmsg(MSG_INFO, "dropping leases for state machine %s", 230 dsmp->dsm_name); 231 232 if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND || 233 dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) { 234 if (dsmp->dsm_dflags & DHCP_IF_BOOTP) { 235 dhcpmsg(MSG_INFO, 236 "used bootp; not writing lease file for %s", 237 dsmp->dsm_name); 238 } else { 239 PKT_LIST *plp[2]; 240 241 plp[0] = dsmp->dsm_ack; 242 plp[1] = dsmp->dsm_orig_ack; 243 if (write_hostconf(dsmp->dsm_name, plp, 2, 244 monosec_to_time(dsmp->dsm_curstart_monosec), 245 dsmp->dsm_isv6) == -1) { 246 dhcpmsg(MSG_ERR, "cannot write %s (reboot will " 247 "not use cached configuration)", 248 ifname_to_hostconf(dsmp->dsm_name, 249 dsmp->dsm_isv6)); 250 } 251 } 252 } 253 deprecate_leases(dsmp); 254 finished_smach(dsmp, DHCP_IPC_SUCCESS); 255 return (1); 256 } 257 258 /* 259 * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE 260 * messages for DHCPv6. When we stop, if there are no 261 * more leases left, then restart the state machine. 262 * 263 * input: dhcp_smach_t *: the state machine messages are being sent from 264 * unsigned int: the number of messages sent so far 265 * output: boolean_t: B_TRUE if retransmissions should stop 266 */ 267 268 static boolean_t 269 stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests) 270 { 271 if (dsmp->dsm_state == RELEASING) { 272 if (n_requests >= DHCPV6_REL_MAX_RC) { 273 dhcpmsg(MSG_INFO, "no Reply to Release, finishing " 274 "transaction on %s", dsmp->dsm_name); 275 finished_smach(dsmp, DHCP_IPC_SUCCESS); 276 return (B_TRUE); 277 } else { 278 return (B_FALSE); 279 } 280 } else { 281 if (n_requests >= DHCPV6_DEC_MAX_RC) { 282 dhcpmsg(MSG_INFO, "no Reply to Decline on %s", 283 dsmp->dsm_name); 284 285 if (dsmp->dsm_leases == NULL) { 286 dhcpmsg(MSG_VERBOSE, "stop_release_decline: " 287 "%s has no leases left; restarting", 288 dsmp->dsm_name); 289 dhcp_restart(dsmp); 290 } 291 return (B_TRUE); 292 } else { 293 return (B_FALSE); 294 } 295 } 296 } 297