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 5*3431Scarlsonj * Common Development and Distribution License (the "License"). 6*3431Scarlsonj * 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 /* 22*3431Scarlsonj * 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> 31*3431Scarlsonj #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> 36*3431Scarlsonj #include <netinet/dhcp6.h> 370Sstevel@tonic-gate #include <dhcpmsg.h> 380Sstevel@tonic-gate #include <dhcp_hostconf.h> 390Sstevel@tonic-gate 40*3431Scarlsonj #include "agent.h" 410Sstevel@tonic-gate #include "packet.h" 420Sstevel@tonic-gate #include "interface.h" 430Sstevel@tonic-gate #include "states.h" 440Sstevel@tonic-gate 45*3431Scarlsonj static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int); 46*3431Scarlsonj 470Sstevel@tonic-gate /* 48*3431Scarlsonj * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the 49*3431Scarlsonj * server to indicate a problem with the offered addresses. 50*3431Scarlsonj * The failing addresses are removed from the leases. 510Sstevel@tonic-gate * 52*3431Scarlsonj * input: dhcp_smach_t *: the state machine sending DECLINE 530Sstevel@tonic-gate * output: void 540Sstevel@tonic-gate */ 550Sstevel@tonic-gate 560Sstevel@tonic-gate void 57*3431Scarlsonj send_declines(dhcp_smach_t *dsmp) 580Sstevel@tonic-gate { 590Sstevel@tonic-gate dhcp_pkt_t *dpkt; 60*3431Scarlsonj dhcp_lease_t *dlp, *dlpn; 61*3431Scarlsonj uint_t nlifs; 62*3431Scarlsonj dhcp_lif_t *lif, *lifn; 63*3431Scarlsonj boolean_t got_one; 640Sstevel@tonic-gate 65*3431Scarlsonj /* 66*3431Scarlsonj * Create an empty DECLINE message. We'll stuff the information into 67*3431Scarlsonj * this message as we find it. 68*3431Scarlsonj */ 69*3431Scarlsonj if (dsmp->dsm_isv6) { 70*3431Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL) 71*3431Scarlsonj return; 72*3431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 73*3431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen); 74*3431Scarlsonj } else { 75*3431Scarlsonj ipaddr_t serverip; 76*3431Scarlsonj 77*3431Scarlsonj /* 78*3431Scarlsonj * If this ack is from BOOTP, then there's no way to send a 79*3431Scarlsonj * decline. Note that since we haven't bound yet, we can't 80*3431Scarlsonj * just check the BOOTP flag. 81*3431Scarlsonj */ 82*3431Scarlsonj if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL) 83*3431Scarlsonj return; 84*3431Scarlsonj 85*3431Scarlsonj if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL) 86*3431Scarlsonj return; 87*3431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 88*3431Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 89*3431Scarlsonj } 900Sstevel@tonic-gate 91*3431Scarlsonj /* 92*3431Scarlsonj * Loop over the leases, looking for ones with now-broken LIFs. Add 93*3431Scarlsonj * each one found to the DECLINE message, and remove it from the list. 94*3431Scarlsonj * Also remove any completely declined leases. 95*3431Scarlsonj */ 96*3431Scarlsonj got_one = B_FALSE; 97*3431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 98*3431Scarlsonj dlpn = dlp->dl_next; 99*3431Scarlsonj lif = dlp->dl_lifs; 100*3431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) { 101*3431Scarlsonj lifn = lif->lif_next; 102*3431Scarlsonj if (lif->lif_declined != NULL) { 103*3431Scarlsonj (void) add_pkt_lif(dpkt, lif, 104*3431Scarlsonj DHCPV6_STAT_UNSPECFAIL, lif->lif_declined); 105*3431Scarlsonj unplumb_lif(lif); 106*3431Scarlsonj got_one = B_TRUE; 107*3431Scarlsonj } 108*3431Scarlsonj } 109*3431Scarlsonj if (dlp->dl_nlifs == 0) 110*3431Scarlsonj remove_lease(dlp); 111*3431Scarlsonj } 1120Sstevel@tonic-gate 113*3431Scarlsonj if (!got_one) 114*3431Scarlsonj return; 115*3431Scarlsonj 116*3431Scarlsonj (void) set_smach_state(dsmp, DECLINING); 1170Sstevel@tonic-gate 118*3431Scarlsonj if (dsmp->dsm_isv6) { 119*3431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 120*3431Scarlsonj stop_release_decline, DHCPV6_DEC_TIMEOUT, 0); 121*3431Scarlsonj } else { 122*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 123*3431Scarlsonj 124*3431Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL); 125*3431Scarlsonj } 1260Sstevel@tonic-gate } 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate /* 1290Sstevel@tonic-gate * dhcp_release(): sends a RELEASE message to a DHCP server and removes 130*3431Scarlsonj * the all interfaces for the given state machine from DHCP 131*3431Scarlsonj * control. Called back by script handler. 1320Sstevel@tonic-gate * 133*3431Scarlsonj * input: dhcp_smach_t *: the state machine to send the RELEASE on and remove 134*3431Scarlsonj * void *: an optional text explanation to send with the message 1350Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 1360Sstevel@tonic-gate */ 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate int 139*3431Scarlsonj dhcp_release(dhcp_smach_t *dsmp, void *arg) 1400Sstevel@tonic-gate { 141*3431Scarlsonj const char *msg = arg; 1420Sstevel@tonic-gate dhcp_pkt_t *dpkt; 143*3431Scarlsonj dhcp_lease_t *dlp; 144*3431Scarlsonj dhcp_lif_t *lif; 145*3431Scarlsonj ipaddr_t serverip; 146*3431Scarlsonj uint_t nlifs; 1470Sstevel@tonic-gate 148*3431Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) || 149*3431Scarlsonj !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) { 150*3431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_INT); 151*3431Scarlsonj return (0); 152*3431Scarlsonj } 1530Sstevel@tonic-gate 154*3431Scarlsonj dhcpmsg(MSG_INFO, "releasing leases for state machine %s", 155*3431Scarlsonj dsmp->dsm_name); 156*3431Scarlsonj (void) set_smach_state(dsmp, RELEASING); 1570Sstevel@tonic-gate 158*3431Scarlsonj if (dsmp->dsm_isv6) { 159*3431Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE); 160*3431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, 161*3431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen); 1620Sstevel@tonic-gate 163*3431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 164*3431Scarlsonj lif = dlp->dl_lifs; 165*3431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; 166*3431Scarlsonj nlifs--, lif = lif->lif_next) { 167*3431Scarlsonj (void) add_pkt_lif(dpkt, lif, 168*3431Scarlsonj DHCPV6_STAT_SUCCESS, NULL); 169*3431Scarlsonj } 170*3431Scarlsonj } 1710Sstevel@tonic-gate 172*3431Scarlsonj /* 173*3431Scarlsonj * Must kill off the leases before attempting to tell the 174*3431Scarlsonj * server. 175*3431Scarlsonj */ 176*3431Scarlsonj deprecate_leases(dsmp); 1770Sstevel@tonic-gate 178*3431Scarlsonj /* 179*3431Scarlsonj * For DHCPv6, this is a transaction, rather than just a 180*3431Scarlsonj * one-shot message. When this transaction is done, we'll 181*3431Scarlsonj * finish the invoking async operation. 182*3431Scarlsonj */ 183*3431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, 184*3431Scarlsonj stop_release_decline, DHCPV6_REL_TIMEOUT, 0); 185*3431Scarlsonj } else { 186*3431Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) { 187*3431Scarlsonj dpkt = init_pkt(dsmp, RELEASE); 188*3431Scarlsonj if (msg != NULL) { 189*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg, 190*3431Scarlsonj strlen(msg) + 1); 191*3431Scarlsonj } 192*3431Scarlsonj lif = dlp->dl_lifs; 193*3431Scarlsonj (void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL); 1940Sstevel@tonic-gate 195*3431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip); 196*3431Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip); 197*3431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 198*3431Scarlsonj (void) send_pkt(dsmp, dpkt, serverip, NULL); 199*3431Scarlsonj } 2000Sstevel@tonic-gate 201*3431Scarlsonj /* 202*3431Scarlsonj * XXX this totally sucks, but since udp is best-effort, 203*3431Scarlsonj * without this delay, there's a good chance that the packet 204*3431Scarlsonj * that we just enqueued for sending will get pitched 205*3431Scarlsonj * when we canonize the interface through remove_smach. 206*3431Scarlsonj */ 207*3431Scarlsonj 208*3431Scarlsonj (void) usleep(500); 209*3431Scarlsonj deprecate_leases(dsmp); 210*3431Scarlsonj 211*3431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 212*3431Scarlsonj } 213*3431Scarlsonj return (1); 2140Sstevel@tonic-gate } 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate /* 217*3431Scarlsonj * dhcp_drop(): drops the interface from DHCP control; callback from script 218*3431Scarlsonj * handler 2190Sstevel@tonic-gate * 220*3431Scarlsonj * input: dhcp_smach_t *: the state machine dropping leases 221*3431Scarlsonj * void *: unused 2220Sstevel@tonic-gate * output: int: always 1 2230Sstevel@tonic-gate */ 2240Sstevel@tonic-gate 225*3431Scarlsonj /* ARGSUSED1 */ 2260Sstevel@tonic-gate int 227*3431Scarlsonj dhcp_drop(dhcp_smach_t *dsmp, void *arg) 2280Sstevel@tonic-gate { 229*3431Scarlsonj dhcpmsg(MSG_INFO, "dropping leases for state machine %s", 230*3431Scarlsonj dsmp->dsm_name); 2310Sstevel@tonic-gate 232*3431Scarlsonj if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND || 233*3431Scarlsonj dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) { 234*3431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_BOOTP) { 235*3431Scarlsonj dhcpmsg(MSG_INFO, 236*3431Scarlsonj "used bootp; not writing lease file for %s", 237*3431Scarlsonj dsmp->dsm_name); 238*3431Scarlsonj } else { 239*3431Scarlsonj PKT_LIST *plp[2]; 2400Sstevel@tonic-gate 241*3431Scarlsonj plp[0] = dsmp->dsm_ack; 242*3431Scarlsonj plp[1] = dsmp->dsm_orig_ack; 243*3431Scarlsonj if (write_hostconf(dsmp->dsm_name, plp, 2, 244*3431Scarlsonj monosec_to_time(dsmp->dsm_curstart_monosec), 245*3431Scarlsonj dsmp->dsm_isv6) == -1) { 2460Sstevel@tonic-gate dhcpmsg(MSG_ERR, "cannot write %s (reboot will " 2470Sstevel@tonic-gate "not use cached configuration)", 248*3431Scarlsonj ifname_to_hostconf(dsmp->dsm_name, 249*3431Scarlsonj dsmp->dsm_isv6)); 250*3431Scarlsonj } 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate } 253*3431Scarlsonj deprecate_leases(dsmp); 254*3431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 2550Sstevel@tonic-gate return (1); 2560Sstevel@tonic-gate } 257*3431Scarlsonj 258*3431Scarlsonj /* 259*3431Scarlsonj * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE 260*3431Scarlsonj * messages for DHCPv6. When we stop, if there are no 261*3431Scarlsonj * more leases left, then restart the state machine. 262*3431Scarlsonj * 263*3431Scarlsonj * input: dhcp_smach_t *: the state machine messages are being sent from 264*3431Scarlsonj * unsigned int: the number of messages sent so far 265*3431Scarlsonj * output: boolean_t: B_TRUE if retransmissions should stop 266*3431Scarlsonj */ 267*3431Scarlsonj 268*3431Scarlsonj static boolean_t 269*3431Scarlsonj stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests) 270*3431Scarlsonj { 271*3431Scarlsonj if (dsmp->dsm_state == RELEASING) { 272*3431Scarlsonj if (n_requests >= DHCPV6_REL_MAX_RC) { 273*3431Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Release, finishing " 274*3431Scarlsonj "transaction on %s", dsmp->dsm_name); 275*3431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 276*3431Scarlsonj return (B_TRUE); 277*3431Scarlsonj } else { 278*3431Scarlsonj return (B_FALSE); 279*3431Scarlsonj } 280*3431Scarlsonj } else { 281*3431Scarlsonj if (n_requests >= DHCPV6_DEC_MAX_RC) { 282*3431Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Decline on %s", 283*3431Scarlsonj dsmp->dsm_name); 284*3431Scarlsonj 285*3431Scarlsonj if (dsmp->dsm_leases == NULL) { 286*3431Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_release_decline: " 287*3431Scarlsonj "%s has no leases left; restarting", 288*3431Scarlsonj dsmp->dsm_name); 289*3431Scarlsonj dhcp_restart(dsmp); 290*3431Scarlsonj } 291*3431Scarlsonj return (B_TRUE); 292*3431Scarlsonj } else { 293*3431Scarlsonj return (B_FALSE); 294*3431Scarlsonj } 295*3431Scarlsonj } 296*3431Scarlsonj } 297