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 /*
22*12989SVasumathi.Sundaram@oracle.COM * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate *
240Sstevel@tonic-gate * DECLINE/RELEASE configuration functionality for the DHCP client.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate #include <sys/types.h>
283431Scarlsonj #include <unistd.h>
290Sstevel@tonic-gate #include <string.h>
300Sstevel@tonic-gate #include <netinet/in.h>
310Sstevel@tonic-gate #include <net/if.h>
320Sstevel@tonic-gate #include <netinet/dhcp.h>
333431Scarlsonj #include <netinet/dhcp6.h>
340Sstevel@tonic-gate #include <dhcpmsg.h>
350Sstevel@tonic-gate #include <dhcp_hostconf.h>
364106Scarlsonj #include <dhcpagent_util.h>
370Sstevel@tonic-gate
383431Scarlsonj #include "agent.h"
390Sstevel@tonic-gate #include "packet.h"
400Sstevel@tonic-gate #include "interface.h"
410Sstevel@tonic-gate #include "states.h"
420Sstevel@tonic-gate
433431Scarlsonj static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int);
443431Scarlsonj
450Sstevel@tonic-gate /*
463431Scarlsonj * send_declines(): sends a DECLINE message (broadcasted for IPv4) to the
473431Scarlsonj * server to indicate a problem with the offered addresses.
483431Scarlsonj * The failing addresses are removed from the leases.
490Sstevel@tonic-gate *
503431Scarlsonj * input: dhcp_smach_t *: the state machine sending DECLINE
510Sstevel@tonic-gate * output: void
520Sstevel@tonic-gate */
530Sstevel@tonic-gate
540Sstevel@tonic-gate void
send_declines(dhcp_smach_t * dsmp)553431Scarlsonj send_declines(dhcp_smach_t *dsmp)
560Sstevel@tonic-gate {
570Sstevel@tonic-gate dhcp_pkt_t *dpkt;
583431Scarlsonj dhcp_lease_t *dlp, *dlpn;
593431Scarlsonj uint_t nlifs;
603431Scarlsonj dhcp_lif_t *lif, *lifn;
613431Scarlsonj boolean_t got_one;
620Sstevel@tonic-gate
633431Scarlsonj /*
643431Scarlsonj * Create an empty DECLINE message. We'll stuff the information into
653431Scarlsonj * this message as we find it.
663431Scarlsonj */
673431Scarlsonj if (dsmp->dsm_isv6) {
683431Scarlsonj if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL)
693431Scarlsonj return;
703431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
713431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen);
723431Scarlsonj } else {
733431Scarlsonj ipaddr_t serverip;
743431Scarlsonj
753431Scarlsonj /*
763431Scarlsonj * If this ack is from BOOTP, then there's no way to send a
773431Scarlsonj * decline. Note that since we haven't bound yet, we can't
783431Scarlsonj * just check the BOOTP flag.
793431Scarlsonj */
803431Scarlsonj if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL)
813431Scarlsonj return;
823431Scarlsonj
833431Scarlsonj if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL)
843431Scarlsonj return;
853431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
863431Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
873431Scarlsonj }
880Sstevel@tonic-gate
893431Scarlsonj /*
903431Scarlsonj * Loop over the leases, looking for ones with now-broken LIFs. Add
913431Scarlsonj * each one found to the DECLINE message, and remove it from the list.
923431Scarlsonj * Also remove any completely declined leases.
933431Scarlsonj */
943431Scarlsonj got_one = B_FALSE;
953431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) {
963431Scarlsonj dlpn = dlp->dl_next;
973431Scarlsonj lif = dlp->dl_lifs;
983431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) {
993431Scarlsonj lifn = lif->lif_next;
1003431Scarlsonj if (lif->lif_declined != NULL) {
1013431Scarlsonj (void) add_pkt_lif(dpkt, lif,
1023431Scarlsonj DHCPV6_STAT_UNSPECFAIL, lif->lif_declined);
1033431Scarlsonj unplumb_lif(lif);
1043431Scarlsonj got_one = B_TRUE;
1053431Scarlsonj }
1063431Scarlsonj }
1073431Scarlsonj if (dlp->dl_nlifs == 0)
1083431Scarlsonj remove_lease(dlp);
1093431Scarlsonj }
1100Sstevel@tonic-gate
1113431Scarlsonj if (!got_one)
1123431Scarlsonj return;
1133431Scarlsonj
1143431Scarlsonj (void) set_smach_state(dsmp, DECLINING);
1150Sstevel@tonic-gate
1163431Scarlsonj if (dsmp->dsm_isv6) {
1173431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
1183431Scarlsonj stop_release_decline, DHCPV6_DEC_TIMEOUT, 0);
1193431Scarlsonj } else {
1203431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
1213431Scarlsonj
1223431Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL);
1233431Scarlsonj }
1240Sstevel@tonic-gate }
1250Sstevel@tonic-gate
1260Sstevel@tonic-gate /*
1270Sstevel@tonic-gate * dhcp_release(): sends a RELEASE message to a DHCP server and removes
1283431Scarlsonj * the all interfaces for the given state machine from DHCP
1293431Scarlsonj * control. Called back by script handler.
1300Sstevel@tonic-gate *
1313431Scarlsonj * input: dhcp_smach_t *: the state machine to send the RELEASE on and remove
1323431Scarlsonj * void *: an optional text explanation to send with the message
1330Sstevel@tonic-gate * output: int: 1 on success, 0 on failure
1340Sstevel@tonic-gate */
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate int
dhcp_release(dhcp_smach_t * dsmp,void * arg)1373431Scarlsonj dhcp_release(dhcp_smach_t *dsmp, void *arg)
1380Sstevel@tonic-gate {
1393431Scarlsonj const char *msg = arg;
1400Sstevel@tonic-gate dhcp_pkt_t *dpkt;
1413431Scarlsonj dhcp_lease_t *dlp;
1423431Scarlsonj dhcp_lif_t *lif;
1433431Scarlsonj ipaddr_t serverip;
1443431Scarlsonj uint_t nlifs;
1450Sstevel@tonic-gate
1463431Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) ||
1473431Scarlsonj !check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) {
1483431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_INT);
1493431Scarlsonj return (0);
1503431Scarlsonj }
1510Sstevel@tonic-gate
1523431Scarlsonj dhcpmsg(MSG_INFO, "releasing leases for state machine %s",
1533431Scarlsonj dsmp->dsm_name);
1543431Scarlsonj (void) set_smach_state(dsmp, RELEASING);
1550Sstevel@tonic-gate
156*12989SVasumathi.Sundaram@oracle.COM (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
157*12989SVasumathi.Sundaram@oracle.COM
1583431Scarlsonj if (dsmp->dsm_isv6) {
1593431Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE);
1603431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
1613431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen);
1620Sstevel@tonic-gate
1633431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1643431Scarlsonj lif = dlp->dl_lifs;
1653431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0;
1663431Scarlsonj nlifs--, lif = lif->lif_next) {
1673431Scarlsonj (void) add_pkt_lif(dpkt, lif,
1683431Scarlsonj DHCPV6_STAT_SUCCESS, NULL);
1693431Scarlsonj }
1703431Scarlsonj }
1710Sstevel@tonic-gate
1723431Scarlsonj /*
1733431Scarlsonj * Must kill off the leases before attempting to tell the
1743431Scarlsonj * server.
1753431Scarlsonj */
1763431Scarlsonj deprecate_leases(dsmp);
1770Sstevel@tonic-gate
1783431Scarlsonj /*
1793431Scarlsonj * For DHCPv6, this is a transaction, rather than just a
1803431Scarlsonj * one-shot message. When this transaction is done, we'll
1813431Scarlsonj * finish the invoking async operation.
1823431Scarlsonj */
1833431Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
1843431Scarlsonj stop_release_decline, DHCPV6_REL_TIMEOUT, 0);
1853431Scarlsonj } else {
1863431Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) {
1873431Scarlsonj dpkt = init_pkt(dsmp, RELEASE);
1883431Scarlsonj if (msg != NULL) {
1893431Scarlsonj (void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
1903431Scarlsonj strlen(msg) + 1);
1913431Scarlsonj }
1923431Scarlsonj lif = dlp->dl_lifs;
1933431Scarlsonj (void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL);
1940Sstevel@tonic-gate
1953431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
1963431Scarlsonj (void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
1973431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
1983431Scarlsonj (void) send_pkt(dsmp, dpkt, serverip, NULL);
1993431Scarlsonj }
2000Sstevel@tonic-gate
2013431Scarlsonj /*
2023431Scarlsonj * XXX this totally sucks, but since udp is best-effort,
2033431Scarlsonj * without this delay, there's a good chance that the packet
2043431Scarlsonj * that we just enqueued for sending will get pitched
2053431Scarlsonj * when we canonize the interface through remove_smach.
2063431Scarlsonj */
2073431Scarlsonj
2083431Scarlsonj (void) usleep(500);
2093431Scarlsonj deprecate_leases(dsmp);
2103431Scarlsonj
2113431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS);
2123431Scarlsonj }
2133431Scarlsonj return (1);
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate /*
2173431Scarlsonj * dhcp_drop(): drops the interface from DHCP control; callback from script
2183431Scarlsonj * handler
2190Sstevel@tonic-gate *
2203431Scarlsonj * input: dhcp_smach_t *: the state machine dropping leases
2213431Scarlsonj * void *: unused
2220Sstevel@tonic-gate * output: int: always 1
2230Sstevel@tonic-gate */
2240Sstevel@tonic-gate
2253431Scarlsonj /* ARGSUSED1 */
2260Sstevel@tonic-gate int
dhcp_drop(dhcp_smach_t * dsmp,void * arg)2273431Scarlsonj dhcp_drop(dhcp_smach_t *dsmp, void *arg)
2280Sstevel@tonic-gate {
2293431Scarlsonj dhcpmsg(MSG_INFO, "dropping leases for state machine %s",
2303431Scarlsonj dsmp->dsm_name);
2310Sstevel@tonic-gate
2323431Scarlsonj if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND ||
2333431Scarlsonj dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
2343431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_BOOTP) {
2353431Scarlsonj dhcpmsg(MSG_INFO,
2363431Scarlsonj "used bootp; not writing lease file for %s",
2373431Scarlsonj dsmp->dsm_name);
2383431Scarlsonj } else {
239*12989SVasumathi.Sundaram@oracle.COM write_lease_to_hostconf(dsmp);
2400Sstevel@tonic-gate }
2414106Scarlsonj } else {
2424106Scarlsonj dhcpmsg(MSG_DEBUG, "%s in state %s; not saving lease",
2434106Scarlsonj dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
2440Sstevel@tonic-gate }
2453431Scarlsonj deprecate_leases(dsmp);
2463431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS);
2470Sstevel@tonic-gate return (1);
2480Sstevel@tonic-gate }
2493431Scarlsonj
2503431Scarlsonj /*
2513431Scarlsonj * stop_release_decline(): decides when to stop retransmitting RELEASE/DECLINE
2523431Scarlsonj * messages for DHCPv6. When we stop, if there are no
2533431Scarlsonj * more leases left, then restart the state machine.
2543431Scarlsonj *
2553431Scarlsonj * input: dhcp_smach_t *: the state machine messages are being sent from
2563431Scarlsonj * unsigned int: the number of messages sent so far
2573431Scarlsonj * output: boolean_t: B_TRUE if retransmissions should stop
2583431Scarlsonj */
2593431Scarlsonj
2603431Scarlsonj static boolean_t
stop_release_decline(dhcp_smach_t * dsmp,unsigned int n_requests)2613431Scarlsonj stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests)
2623431Scarlsonj {
2633431Scarlsonj if (dsmp->dsm_state == RELEASING) {
2643431Scarlsonj if (n_requests >= DHCPV6_REL_MAX_RC) {
2653431Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Release, finishing "
2663431Scarlsonj "transaction on %s", dsmp->dsm_name);
2673431Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS);
2683431Scarlsonj return (B_TRUE);
2693431Scarlsonj } else {
2703431Scarlsonj return (B_FALSE);
2713431Scarlsonj }
2723431Scarlsonj } else {
2733431Scarlsonj if (n_requests >= DHCPV6_DEC_MAX_RC) {
2743431Scarlsonj dhcpmsg(MSG_INFO, "no Reply to Decline on %s",
2753431Scarlsonj dsmp->dsm_name);
2763431Scarlsonj
2773431Scarlsonj if (dsmp->dsm_leases == NULL) {
2783431Scarlsonj dhcpmsg(MSG_VERBOSE, "stop_release_decline: "
2794516Scarlsonj "%s has no leases left", dsmp->dsm_name);
2803431Scarlsonj dhcp_restart(dsmp);
2813431Scarlsonj }
2823431Scarlsonj return (B_TRUE);
2833431Scarlsonj } else {
2843431Scarlsonj return (B_FALSE);
2853431Scarlsonj }
2863431Scarlsonj }
2873431Scarlsonj }
288