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
send_declines(dhcp_smach_t * dsmp)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
dhcp_release(dhcp_smach_t * dsmp,void * arg)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
dhcp_drop(dhcp_smach_t * dsmp,void * arg)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
stop_release_decline(dhcp_smach_t * dsmp,unsigned int n_requests)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