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 5d04ccbb3Scarlsonj * Common Development and Distribution License (the "License"). 6d04ccbb3Scarlsonj * 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 /* 22*0a3e1f6cSVasumathi Sundaram * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 237c478bd9Sstevel@tonic-gate * 247c478bd9Sstevel@tonic-gate * DECLINE/RELEASE configuration functionality for the DHCP client. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #include <sys/types.h> 28d04ccbb3Scarlsonj #include <unistd.h> 297c478bd9Sstevel@tonic-gate #include <string.h> 307c478bd9Sstevel@tonic-gate #include <netinet/in.h> 317c478bd9Sstevel@tonic-gate #include <net/if.h> 327c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h> 33d04ccbb3Scarlsonj #include <netinet/dhcp6.h> 347c478bd9Sstevel@tonic-gate #include <dhcpmsg.h> 357c478bd9Sstevel@tonic-gate #include <dhcp_hostconf.h> 36cfb9c9abScarlsonj #include <dhcpagent_util.h> 377c478bd9Sstevel@tonic-gate 38d04ccbb3Scarlsonj #include "agent.h" 397c478bd9Sstevel@tonic-gate #include "packet.h" 407c478bd9Sstevel@tonic-gate #include "interface.h" 417c478bd9Sstevel@tonic-gate #include "states.h" 427c478bd9Sstevel@tonic-gate 43d04ccbb3Scarlsonj static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int); 44d04ccbb3Scarlsonj 457c478bd9Sstevel@tonic-gate /* 46d04ccbb3Scarlsonj * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the 47d04ccbb3Scarlsonj * server to indicate a problem with the offered addresses. 48d04ccbb3Scarlsonj * The failing addresses are removed from the leases. 497c478bd9Sstevel@tonic-gate * 50d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine sending DECLINE 517c478bd9Sstevel@tonic-gate * output: void 527c478bd9Sstevel@tonic-gate */ 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate void 55d04ccbb3Scarlsonj send_declines(dhcp_smach_t *dsmp) 567c478bd9Sstevel@tonic-gate { 577c478bd9Sstevel@tonic-gate dhcp_pkt_t *dpkt; 58d04ccbb3Scarlsonj dhcp_lease_t *dlp, *dlpn; 59d04ccbb3Scarlsonj uint_t nlifs; 60d04ccbb3Scarlsonj dhcp_lif_t *lif, *lifn; 61d04ccbb3Scarlsonj boolean_t got_one; 627c478bd9Sstevel@tonic-gate 63d04ccbb3Scarlsonj /* 64d04ccbb3Scarlsonj * Create an empty DECLINE message. We'll stuff the information into 65d04ccbb3Scarlsonj * this message as we find it. 66d04ccbb3Scarlsonj */ 67d04ccbb3Scarlsonj if (dsmp->dsm_isv6) { 68d04ccbb3Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL) 69d04ccbb3Scarlsonj return; 70d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 71d04ccbb3Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen); 72d04ccbb3Scarlsonj } else { 73d04ccbb3Scarlsonj ipaddr_t serverip; 747c478bd9Sstevel@tonic-gate 75d04ccbb3Scarlsonj /* 76d04ccbb3Scarlsonj * If this ack is from BOOTP, then there's no way to send a 77d04ccbb3Scarlsonj * decline. Note that since we haven't bound yet, we can't 78d04ccbb3Scarlsonj * just check the BOOTP flag. 79d04ccbb3Scarlsonj */ 80d04ccbb3Scarlsonj if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL) 81d04ccbb3Scarlsonj return; 827c478bd9Sstevel@tonic-gate 83d04ccbb3Scarlsonj if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL) 84d04ccbb3Scarlsonj return; 85d04ccbb3Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 86d04ccbb3Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 87d04ccbb3Scarlsonj } 887c478bd9Sstevel@tonic-gate 89d04ccbb3Scarlsonj /* 90d04ccbb3Scarlsonj * Loop over the leases, looking for ones with now-broken LIFs. Add 91d04ccbb3Scarlsonj * each one found to the DECLINE message, and remove it from the list. 92d04ccbb3Scarlsonj * Also remove any completely declined leases. 93d04ccbb3Scarlsonj */ 94d04ccbb3Scarlsonj got_one = B_FALSE; 95d04ccbb3Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 96d04ccbb3Scarlsonj dlpn = dlp->dl_next; 97d04ccbb3Scarlsonj lif = dlp->dl_lifs; 98d04ccbb3Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) { 99d04ccbb3Scarlsonj lifn = lif->lif_next; 100d04ccbb3Scarlsonj if (lif->lif_declined != NULL) { 101d04ccbb3Scarlsonj (void) add_pkt_lif(dpkt, lif, 102d04ccbb3Scarlsonj DHCPV6_STAT_UNSPECFAIL, lif->lif_declined); 103d04ccbb3Scarlsonj unplumb_lif(lif); 104d04ccbb3Scarlsonj got_one = B_TRUE; 105d04ccbb3Scarlsonj } 106d04ccbb3Scarlsonj } 107d04ccbb3Scarlsonj if (dlp->dl_nlifs == 0) 108d04ccbb3Scarlsonj remove_lease(dlp); 109d04ccbb3Scarlsonj } 110d04ccbb3Scarlsonj 111d04ccbb3Scarlsonj if (!got_one) 112d04ccbb3Scarlsonj return; 113d04ccbb3Scarlsonj 114d04ccbb3Scarlsonj (void) set_smach_state(dsmp, DECLINING); 115d04ccbb3Scarlsonj 116d04ccbb3Scarlsonj if (dsmp->dsm_isv6) { 117d04ccbb3Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 118d04ccbb3Scarlsonj stop_release_decline, DHCPV6_DEC_TIMEOUT, 0); 119d04ccbb3Scarlsonj } else { 120d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 121d04ccbb3Scarlsonj 122d04ccbb3Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL); 123d04ccbb3Scarlsonj } 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate /* 1277c478bd9Sstevel@tonic-gate * dhcp_release(): sends a RELEASE message to a DHCP server and removes 128d04ccbb3Scarlsonj * the all interfaces for the given state machine from DHCP 129d04ccbb3Scarlsonj * control. Called back by script handler. 1307c478bd9Sstevel@tonic-gate * 131d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to send the RELEASE on and remove 132d04ccbb3Scarlsonj * void *: an optional text explanation to send with the message 1337c478bd9Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 1347c478bd9Sstevel@tonic-gate */ 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate int 137d04ccbb3Scarlsonj dhcp_release(dhcp_smach_t *dsmp, void *arg) 1387c478bd9Sstevel@tonic-gate { 139d04ccbb3Scarlsonj const char *msg = arg; 1407c478bd9Sstevel@tonic-gate dhcp_pkt_t *dpkt; 141d04ccbb3Scarlsonj dhcp_lease_t *dlp; 142d04ccbb3Scarlsonj dhcp_lif_t *lif; 143d04ccbb3Scarlsonj ipaddr_t serverip; 144d04ccbb3Scarlsonj uint_t nlifs; 1457c478bd9Sstevel@tonic-gate 146d04ccbb3Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) || 147d04ccbb3Scarlsonj !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) { 148d04ccbb3Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_INT); 149d04ccbb3Scarlsonj return (0); 150d04ccbb3Scarlsonj } 1517c478bd9Sstevel@tonic-gate 152d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "releasing leases for state machine %s", 153d04ccbb3Scarlsonj dsmp->dsm_name); 154d04ccbb3Scarlsonj (void) set_smach_state(dsmp, RELEASING); 1557c478bd9Sstevel@tonic-gate 156*0a3e1f6cSVasumathi Sundaram (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 157*0a3e1f6cSVasumathi Sundaram 158d04ccbb3Scarlsonj if (dsmp->dsm_isv6) { 159d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE); 160d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 161d04ccbb3Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen); 1627c478bd9Sstevel@tonic-gate 163d04ccbb3Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 164d04ccbb3Scarlsonj lif = dlp->dl_lifs; 165d04ccbb3Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; 166d04ccbb3Scarlsonj nlifs--, lif = lif->lif_next) { 167d04ccbb3Scarlsonj (void) add_pkt_lif(dpkt, lif, 168d04ccbb3Scarlsonj DHCPV6_STAT_SUCCESS, NULL); 169d04ccbb3Scarlsonj } 170d04ccbb3Scarlsonj } 1717c478bd9Sstevel@tonic-gate 172d04ccbb3Scarlsonj /* 173d04ccbb3Scarlsonj * Must kill off the leases before attempting to tell the 174d04ccbb3Scarlsonj * server. 175d04ccbb3Scarlsonj */ 176d04ccbb3Scarlsonj deprecate_leases(dsmp); 1777c478bd9Sstevel@tonic-gate 178d04ccbb3Scarlsonj /* 179d04ccbb3Scarlsonj * For DHCPv6, this is a transaction, rather than just a 180d04ccbb3Scarlsonj * one-shot message. When this transaction is done, we'll 181d04ccbb3Scarlsonj * finish the invoking async operation. 182d04ccbb3Scarlsonj */ 183d04ccbb3Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 184d04ccbb3Scarlsonj stop_release_decline, DHCPV6_REL_TIMEOUT, 0); 185d04ccbb3Scarlsonj } else { 186d04ccbb3Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) { 187d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, RELEASE); 188d04ccbb3Scarlsonj if (msg != NULL) { 189d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 190d04ccbb3Scarlsonj strlen(msg) + 1); 191d04ccbb3Scarlsonj } 192d04ccbb3Scarlsonj lif = dlp->dl_lifs; 193d04ccbb3Scarlsonj (void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL); 1947c478bd9Sstevel@tonic-gate 195d04ccbb3Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 196d04ccbb3Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 197d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 198d04ccbb3Scarlsonj (void) send_pkt(dsmp, dpkt, serverip, NULL); 199d04ccbb3Scarlsonj } 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate /* 2027c478bd9Sstevel@tonic-gate * XXX this totally sucks, but since udp is best-effort, 2037c478bd9Sstevel@tonic-gate * without this delay, there's a good chance that the packet 2047c478bd9Sstevel@tonic-gate * that we just enqueued for sending will get pitched 205d04ccbb3Scarlsonj * when we canonize the interface through remove_smach. 2067c478bd9Sstevel@tonic-gate */ 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate (void) usleep(500); 209d04ccbb3Scarlsonj deprecate_leases(dsmp); 2107c478bd9Sstevel@tonic-gate 211d04ccbb3Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 212d04ccbb3Scarlsonj } 213d04ccbb3Scarlsonj return (1); 2147c478bd9Sstevel@tonic-gate } 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 217d04ccbb3Scarlsonj * dhcp_drop(): drops the interface from DHCP control; callback from script 218d04ccbb3Scarlsonj * handler 2197c478bd9Sstevel@tonic-gate * 220d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine dropping leases 221d04ccbb3Scarlsonj * void *: unused 2227c478bd9Sstevel@tonic-gate * output: int: always 1 2237c478bd9Sstevel@tonic-gate */ 2247c478bd9Sstevel@tonic-gate 225d04ccbb3Scarlsonj /* ARGSUSED1 */ 2267c478bd9Sstevel@tonic-gate int 227d04ccbb3Scarlsonj dhcp_drop(dhcp_smach_t *dsmp, void *arg) 2287c478bd9Sstevel@tonic-gate { 229d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "dropping leases for state machine %s", 230d04ccbb3Scarlsonj dsmp->dsm_name); 231d04ccbb3Scarlsonj 232d04ccbb3Scarlsonj if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND || 233d04ccbb3Scarlsonj dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) { 234d04ccbb3Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_BOOTP) { 235d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, 236d04ccbb3Scarlsonj "used bootp; not writing lease file for %s", 237d04ccbb3Scarlsonj dsmp->dsm_name); 238d04ccbb3Scarlsonj } else { 239*0a3e1f6cSVasumathi Sundaram write_lease_to_hostconf(dsmp); 2407c478bd9Sstevel@tonic-gate } 241cfb9c9abScarlsonj } else { 242cfb9c9abScarlsonj dhcpmsg(MSG_DEBUG, "%s in state %s; not saving lease", 243cfb9c9abScarlsonj dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state)); 244d04ccbb3Scarlsonj } 245d04ccbb3Scarlsonj deprecate_leases(dsmp); 246d04ccbb3Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 2477c478bd9Sstevel@tonic-gate return (1); 2487c478bd9Sstevel@tonic-gate } 249d04ccbb3Scarlsonj 250d04ccbb3Scarlsonj /* 251d04ccbb3Scarlsonj * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE 252d04ccbb3Scarlsonj * messages for DHCPv6. When we stop, if there are no 253d04ccbb3Scarlsonj * more leases left, then restart the state machine. 254d04ccbb3Scarlsonj * 255d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine messages are being sent from 256d04ccbb3Scarlsonj * unsigned int: the number of messages sent so far 257d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if retransmissions should stop 258d04ccbb3Scarlsonj */ 259d04ccbb3Scarlsonj 260d04ccbb3Scarlsonj static boolean_t 261d04ccbb3Scarlsonj stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests) 262d04ccbb3Scarlsonj { 263d04ccbb3Scarlsonj if (dsmp->dsm_state == RELEASING) { 264d04ccbb3Scarlsonj if (n_requests >= DHCPV6_REL_MAX_RC) { 265d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Release, finishing " 266d04ccbb3Scarlsonj "transaction on %s", dsmp->dsm_name); 267d04ccbb3Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 268d04ccbb3Scarlsonj return (B_TRUE); 269d04ccbb3Scarlsonj } else { 270d04ccbb3Scarlsonj return (B_FALSE); 271d04ccbb3Scarlsonj } 272d04ccbb3Scarlsonj } else { 273d04ccbb3Scarlsonj if (n_requests >= DHCPV6_DEC_MAX_RC) { 274d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Decline on %s", 275d04ccbb3Scarlsonj dsmp->dsm_name); 276d04ccbb3Scarlsonj 277d04ccbb3Scarlsonj if (dsmp->dsm_leases == NULL) { 278d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_release_decline: " 279906cb642Scarlsonj "%s has no leases left", dsmp->dsm_name); 280d04ccbb3Scarlsonj dhcp_restart(dsmp); 281d04ccbb3Scarlsonj } 282d04ccbb3Scarlsonj return (B_TRUE); 283d04ccbb3Scarlsonj } else { 284d04ccbb3Scarlsonj return (B_FALSE); 285d04ccbb3Scarlsonj } 286d04ccbb3Scarlsonj } 287d04ccbb3Scarlsonj } 288