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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 23 * 24 * DECLINE/RELEASE configuration functionality for the DHCP client. 25 */ 26 27 #include <sys/types.h> 28 #include <unistd.h> 29 #include <string.h> 30 #include <netinet/in.h> 31 #include <net/if.h> 32 #include <netinet/dhcp.h> 33 #include <netinet/dhcp6.h> 34 #include <dhcpmsg.h> 35 #include <dhcp_hostconf.h> 36 #include <dhcpagent_util.h> 37 38 #include "agent.h" 39 #include "packet.h" 40 #include "interface.h" 41 #include "states.h" 42 43 static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int); 44 45 /* 46 * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the 47 * server to indicate a problem with the offered addresses. 48 * The failing addresses are removed from the leases. 49 * 50 * input: dhcp_smach_t *: the state machine sending DECLINE 51 * output: void 52 */ 53 54 void 55 send_declines(dhcp_smach_t *dsmp) 56 { 57 dhcp_pkt_t *dpkt; 58 dhcp_lease_t *dlp, *dlpn; 59 uint_t nlifs; 60 dhcp_lif_t *lif, *lifn; 61 boolean_t got_one; 62 63 /* 64 * Create an empty DECLINE message. We'll stuff the information into 65 * this message as we find it. 66 */ 67 if (dsmp->dsm_isv6) { 68 if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL) 69 return; 70 (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 71 dsmp->dsm_serverid, dsmp->dsm_serveridlen); 72 } else { 73 ipaddr_t serverip; 74 75 /* 76 * If this ack is from BOOTP, then there's no way to send a 77 * decline. Note that since we haven't bound yet, we can't 78 * just check the BOOTP flag. 79 */ 80 if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL) 81 return; 82 83 if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL) 84 return; 85 IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 86 (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 87 } 88 89 /* 90 * Loop over the leases, looking for ones with now-broken LIFs. Add 91 * each one found to the DECLINE message, and remove it from the list. 92 * Also remove any completely declined leases. 93 */ 94 got_one = B_FALSE; 95 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 96 dlpn = dlp->dl_next; 97 lif = dlp->dl_lifs; 98 for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) { 99 lifn = lif->lif_next; 100 if (lif->lif_declined != NULL) { 101 (void) add_pkt_lif(dpkt, lif, 102 DHCPV6_STAT_UNSPECFAIL, lif->lif_declined); 103 unplumb_lif(lif); 104 got_one = B_TRUE; 105 } 106 } 107 if (dlp->dl_nlifs == 0) 108 remove_lease(dlp); 109 } 110 111 if (!got_one) 112 return; 113 114 (void) set_smach_state(dsmp, DECLINING); 115 116 if (dsmp->dsm_isv6) { 117 (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 118 stop_release_decline, DHCPV6_DEC_TIMEOUT, 0); 119 } else { 120 (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 121 122 (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL); 123 } 124 } 125 126 /* 127 * dhcp_release(): sends a RELEASE message to a DHCP server and removes 128 * the all interfaces for the given state machine from DHCP 129 * control. Called back by script handler. 130 * 131 * input: dhcp_smach_t *: the state machine to send the RELEASE on and remove 132 * void *: an optional text explanation to send with the message 133 * output: int: 1 on success, 0 on failure 134 */ 135 136 int 137 dhcp_release(dhcp_smach_t *dsmp, void *arg) 138 { 139 const char *msg = arg; 140 dhcp_pkt_t *dpkt; 141 dhcp_lease_t *dlp; 142 dhcp_lif_t *lif; 143 ipaddr_t serverip; 144 uint_t nlifs; 145 146 if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) || 147 !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) { 148 ipc_action_finish(dsmp, DHCP_IPC_E_INT); 149 return (0); 150 } 151 152 dhcpmsg(MSG_INFO, "releasing leases for state machine %s", 153 dsmp->dsm_name); 154 (void) set_smach_state(dsmp, RELEASING); 155 156 (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 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 write_lease_to_hostconf(dsmp); 240 } 241 } else { 242 dhcpmsg(MSG_DEBUG, "%s in state %s; not saving lease", 243 dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state)); 244 } 245 deprecate_leases(dsmp); 246 finished_smach(dsmp, DHCP_IPC_SUCCESS); 247 return (1); 248 } 249 250 /* 251 * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE 252 * messages for DHCPv6. When we stop, if there are no 253 * more leases left, then restart the state machine. 254 * 255 * input: dhcp_smach_t *: the state machine messages are being sent from 256 * unsigned int: the number of messages sent so far 257 * output: boolean_t: B_TRUE if retransmissions should stop 258 */ 259 260 static boolean_t 261 stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests) 262 { 263 if (dsmp->dsm_state == RELEASING) { 264 if (n_requests >= DHCPV6_REL_MAX_RC) { 265 dhcpmsg(MSG_INFO, "no Reply to Release, finishing " 266 "transaction on %s", dsmp->dsm_name); 267 finished_smach(dsmp, DHCP_IPC_SUCCESS); 268 return (B_TRUE); 269 } else { 270 return (B_FALSE); 271 } 272 } else { 273 if (n_requests >= DHCPV6_DEC_MAX_RC) { 274 dhcpmsg(MSG_INFO, "no Reply to Decline on %s", 275 dsmp->dsm_name); 276 277 if (dsmp->dsm_leases == NULL) { 278 dhcpmsg(MSG_VERBOSE, "stop_release_decline: " 279 "%s has no leases left", dsmp->dsm_name); 280 dhcp_restart(dsmp); 281 } 282 return (B_TRUE); 283 } else { 284 return (B_FALSE); 285 } 286 } 287 } 288