10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53431Scarlsonj * Common Development and Distribution License (the "License"). 63431Scarlsonj * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate * DECLINE/RELEASE configuration functionality for the DHCP client. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/types.h> 313431Scarlsonj #include <unistd.h> 320Sstevel@tonic-gate #include <string.h> 330Sstevel@tonic-gate #include <netinet/in.h> 340Sstevel@tonic-gate #include <net/if.h> 350Sstevel@tonic-gate #include <netinet/dhcp.h> 363431Scarlsonj #include <netinet/dhcp6.h> 370Sstevel@tonic-gate #include <dhcpmsg.h> 380Sstevel@tonic-gate #include <dhcp_hostconf.h> 39*4106Scarlsonj #include <dhcpagent_util.h> 400Sstevel@tonic-gate 413431Scarlsonj #include "agent.h" 420Sstevel@tonic-gate #include "packet.h" 430Sstevel@tonic-gate #include "interface.h" 440Sstevel@tonic-gate #include "states.h" 450Sstevel@tonic-gate 463431Scarlsonj static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int); 473431Scarlsonj 480Sstevel@tonic-gate /* 493431Scarlsonj * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the 503431Scarlsonj * server to indicate a problem with the offered addresses. 513431Scarlsonj * The failing addresses are removed from the leases. 520Sstevel@tonic-gate * 533431Scarlsonj * input: dhcp_smach_t *: the state machine sending DECLINE 540Sstevel@tonic-gate * output: void 550Sstevel@tonic-gate */ 560Sstevel@tonic-gate 570Sstevel@tonic-gate void 583431Scarlsonj send_declines(dhcp_smach_t *dsmp) 590Sstevel@tonic-gate { 600Sstevel@tonic-gate dhcp_pkt_t *dpkt; 613431Scarlsonj dhcp_lease_t *dlp, *dlpn; 623431Scarlsonj uint_t nlifs; 633431Scarlsonj dhcp_lif_t *lif, *lifn; 643431Scarlsonj boolean_t got_one; 650Sstevel@tonic-gate 663431Scarlsonj /* 673431Scarlsonj * Create an empty DECLINE message. We'll stuff the information into 683431Scarlsonj * this message as we find it. 693431Scarlsonj */ 703431Scarlsonj if (dsmp->dsm_isv6) { 713431Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL) 723431Scarlsonj return; 733431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 743431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen); 753431Scarlsonj } else { 763431Scarlsonj ipaddr_t serverip; 773431Scarlsonj 783431Scarlsonj /* 793431Scarlsonj * If this ack is from BOOTP, then there's no way to send a 803431Scarlsonj * decline. Note that since we haven't bound yet, we can't 813431Scarlsonj * just check the BOOTP flag. 823431Scarlsonj */ 833431Scarlsonj if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL) 843431Scarlsonj return; 853431Scarlsonj 863431Scarlsonj if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL) 873431Scarlsonj return; 883431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 893431Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 903431Scarlsonj } 910Sstevel@tonic-gate 923431Scarlsonj /* 933431Scarlsonj * Loop over the leases, looking for ones with now-broken LIFs. Add 943431Scarlsonj * each one found to the DECLINE message, and remove it from the list. 953431Scarlsonj * Also remove any completely declined leases. 963431Scarlsonj */ 973431Scarlsonj got_one = B_FALSE; 983431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 993431Scarlsonj dlpn = dlp->dl_next; 1003431Scarlsonj lif = dlp->dl_lifs; 1013431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) { 1023431Scarlsonj lifn = lif->lif_next; 1033431Scarlsonj if (lif->lif_declined != NULL) { 1043431Scarlsonj (void) add_pkt_lif(dpkt, lif, 1053431Scarlsonj DHCPV6_STAT_UNSPECFAIL, lif->lif_declined); 1063431Scarlsonj unplumb_lif(lif); 1073431Scarlsonj got_one = B_TRUE; 1083431Scarlsonj } 1093431Scarlsonj } 1103431Scarlsonj if (dlp->dl_nlifs == 0) 1113431Scarlsonj remove_lease(dlp); 1123431Scarlsonj } 1130Sstevel@tonic-gate 1143431Scarlsonj if (!got_one) 1153431Scarlsonj return; 1163431Scarlsonj 1173431Scarlsonj (void) set_smach_state(dsmp, DECLINING); 1180Sstevel@tonic-gate 1193431Scarlsonj if (dsmp->dsm_isv6) { 1203431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 1213431Scarlsonj stop_release_decline, DHCPV6_DEC_TIMEOUT, 0); 1223431Scarlsonj } else { 1233431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 1243431Scarlsonj 1253431Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL); 1263431Scarlsonj } 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate /* 1300Sstevel@tonic-gate * dhcp_release(): sends a RELEASE message to a DHCP server and removes 1313431Scarlsonj * the all interfaces for the given state machine from DHCP 1323431Scarlsonj * control. Called back by script handler. 1330Sstevel@tonic-gate * 1343431Scarlsonj * input: dhcp_smach_t *: the state machine to send the RELEASE on and remove 1353431Scarlsonj * void *: an optional text explanation to send with the message 1360Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 1370Sstevel@tonic-gate */ 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate int 1403431Scarlsonj dhcp_release(dhcp_smach_t *dsmp, void *arg) 1410Sstevel@tonic-gate { 1423431Scarlsonj const char *msg = arg; 1430Sstevel@tonic-gate dhcp_pkt_t *dpkt; 1443431Scarlsonj dhcp_lease_t *dlp; 1453431Scarlsonj dhcp_lif_t *lif; 1463431Scarlsonj ipaddr_t serverip; 1473431Scarlsonj uint_t nlifs; 1480Sstevel@tonic-gate 1493431Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) || 1503431Scarlsonj !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) { 1513431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_INT); 1523431Scarlsonj return (0); 1533431Scarlsonj } 1540Sstevel@tonic-gate 1553431Scarlsonj dhcpmsg(MSG_INFO, "releasing leases for state machine %s", 1563431Scarlsonj dsmp->dsm_name); 1573431Scarlsonj (void) set_smach_state(dsmp, RELEASING); 1580Sstevel@tonic-gate 1593431Scarlsonj if (dsmp->dsm_isv6) { 1603431Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE); 1613431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 1623431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen); 1630Sstevel@tonic-gate 1643431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 1653431Scarlsonj lif = dlp->dl_lifs; 1663431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; 1673431Scarlsonj nlifs--, lif = lif->lif_next) { 1683431Scarlsonj (void) add_pkt_lif(dpkt, lif, 1693431Scarlsonj DHCPV6_STAT_SUCCESS, NULL); 1703431Scarlsonj } 1713431Scarlsonj } 1720Sstevel@tonic-gate 1733431Scarlsonj /* 1743431Scarlsonj * Must kill off the leases before attempting to tell the 1753431Scarlsonj * server. 1763431Scarlsonj */ 1773431Scarlsonj deprecate_leases(dsmp); 1780Sstevel@tonic-gate 1793431Scarlsonj /* 1803431Scarlsonj * For DHCPv6, this is a transaction, rather than just a 1813431Scarlsonj * one-shot message. When this transaction is done, we'll 1823431Scarlsonj * finish the invoking async operation. 1833431Scarlsonj */ 1843431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 1853431Scarlsonj stop_release_decline, DHCPV6_REL_TIMEOUT, 0); 1863431Scarlsonj } else { 1873431Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) { 1883431Scarlsonj dpkt = init_pkt(dsmp, RELEASE); 1893431Scarlsonj if (msg != NULL) { 1903431Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 1913431Scarlsonj strlen(msg) + 1); 1923431Scarlsonj } 1933431Scarlsonj lif = dlp->dl_lifs; 1943431Scarlsonj (void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL); 1950Sstevel@tonic-gate 1963431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 1973431Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 1983431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 1993431Scarlsonj (void) send_pkt(dsmp, dpkt, serverip, NULL); 2003431Scarlsonj } 2010Sstevel@tonic-gate 2023431Scarlsonj /* 2033431Scarlsonj * XXX this totally sucks, but since udp is best-effort, 2043431Scarlsonj * without this delay, there's a good chance that the packet 2053431Scarlsonj * that we just enqueued for sending will get pitched 2063431Scarlsonj * when we canonize the interface through remove_smach. 2073431Scarlsonj */ 2083431Scarlsonj 2093431Scarlsonj (void) usleep(500); 2103431Scarlsonj deprecate_leases(dsmp); 2113431Scarlsonj 2123431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 2133431Scarlsonj } 2143431Scarlsonj return (1); 2150Sstevel@tonic-gate } 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate /* 2183431Scarlsonj * dhcp_drop(): drops the interface from DHCP control; callback from script 2193431Scarlsonj * handler 2200Sstevel@tonic-gate * 2213431Scarlsonj * input: dhcp_smach_t *: the state machine dropping leases 2223431Scarlsonj * void *: unused 2230Sstevel@tonic-gate * output: int: always 1 2240Sstevel@tonic-gate */ 2250Sstevel@tonic-gate 2263431Scarlsonj /* ARGSUSED1 */ 2270Sstevel@tonic-gate int 2283431Scarlsonj dhcp_drop(dhcp_smach_t *dsmp, void *arg) 2290Sstevel@tonic-gate { 2303431Scarlsonj dhcpmsg(MSG_INFO, "dropping leases for state machine %s", 2313431Scarlsonj dsmp->dsm_name); 2320Sstevel@tonic-gate 2333431Scarlsonj if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND || 2343431Scarlsonj dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) { 2353431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_BOOTP) { 2363431Scarlsonj dhcpmsg(MSG_INFO, 2373431Scarlsonj "used bootp; not writing lease file for %s", 2383431Scarlsonj dsmp->dsm_name); 2393431Scarlsonj } else { 2403431Scarlsonj PKT_LIST *plp[2]; 241*4106Scarlsonj const char *hcfile; 2420Sstevel@tonic-gate 243*4106Scarlsonj hcfile = ifname_to_hostconf(dsmp->dsm_name, 244*4106Scarlsonj dsmp->dsm_isv6); 2453431Scarlsonj plp[0] = dsmp->dsm_ack; 2463431Scarlsonj plp[1] = dsmp->dsm_orig_ack; 2473431Scarlsonj if (write_hostconf(dsmp->dsm_name, plp, 2, 2483431Scarlsonj monosec_to_time(dsmp->dsm_curstart_monosec), 249*4106Scarlsonj dsmp->dsm_isv6) != -1) { 250*4106Scarlsonj dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile); 251*4106Scarlsonj } else if (errno == EROFS) { 252*4106Scarlsonj dhcpmsg(MSG_DEBUG, "%s is on a read-only file " 253*4106Scarlsonj "system; not saving lease", hcfile); 254*4106Scarlsonj } else { 2550Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot write %s (reboot will " 256*4106Scarlsonj "not use cached configuration)", hcfile); 2573431Scarlsonj } 2580Sstevel@tonic-gate } 259*4106Scarlsonj } else { 260*4106Scarlsonj dhcpmsg(MSG_DEBUG, "%s in state %s; not saving lease", 261*4106Scarlsonj dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state)); 2620Sstevel@tonic-gate } 2633431Scarlsonj deprecate_leases(dsmp); 2643431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 2650Sstevel@tonic-gate return (1); 2660Sstevel@tonic-gate } 2673431Scarlsonj 2683431Scarlsonj /* 2693431Scarlsonj * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE 2703431Scarlsonj * messages for DHCPv6. When we stop, if there are no 2713431Scarlsonj * more leases left, then restart the state machine. 2723431Scarlsonj * 2733431Scarlsonj * input: dhcp_smach_t *: the state machine messages are being sent from 2743431Scarlsonj * unsigned int: the number of messages sent so far 2753431Scarlsonj * output: boolean_t: B_TRUE if retransmissions should stop 2763431Scarlsonj */ 2773431Scarlsonj 2783431Scarlsonj static boolean_t 2793431Scarlsonj stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests) 2803431Scarlsonj { 2813431Scarlsonj if (dsmp->dsm_state == RELEASING) { 2823431Scarlsonj if (n_requests >= DHCPV6_REL_MAX_RC) { 2833431Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Release, finishing " 2843431Scarlsonj "transaction on %s", dsmp->dsm_name); 2853431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 2863431Scarlsonj return (B_TRUE); 2873431Scarlsonj } else { 2883431Scarlsonj return (B_FALSE); 2893431Scarlsonj } 2903431Scarlsonj } else { 2913431Scarlsonj if (n_requests >= DHCPV6_DEC_MAX_RC) { 2923431Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Decline on %s", 2933431Scarlsonj dsmp->dsm_name); 2943431Scarlsonj 2953431Scarlsonj if (dsmp->dsm_leases == NULL) { 2963431Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_release_decline: " 2973431Scarlsonj "%s has no leases left; restarting", 2983431Scarlsonj dsmp->dsm_name); 2993431Scarlsonj dhcp_restart(dsmp); 3003431Scarlsonj } 3013431Scarlsonj return (B_TRUE); 3023431Scarlsonj } else { 3033431Scarlsonj return (B_FALSE); 3043431Scarlsonj } 3053431Scarlsonj } 3063431Scarlsonj } 307