xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/bootp.c (revision 12864:78ceb0079fba)
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) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <stdarg.h>
29 #include <sys/types.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <sys/socket.h>
35 #include <net/if.h>
36 #include <netinet/in.h>
37 #include <netinet/if_ether.h>
38 #include <arpa/inet.h>
39 #include <nss_dbdefs.h>
40 #include <netinet/dhcp.h>
41 #include <netdb.h>
42 #include <dhcp_symbol.h>
43 #include "dhcpd.h"
44 #include "per_dnet.h"
45 #include "interfaces.h"
46 #include <locale.h>
47 
48 /*
49  * This file contains the code which implements the BOOTP compatibility.
50  */
51 
52 /*
53  * We are guaranteed that the packet received is a BOOTP request packet,
54  * e.g., *NOT* a DHCP packet.
55  */
56 void
bootp(dsvc_clnt_t * pcd,PKT_LIST * plp)57 bootp(dsvc_clnt_t *pcd, PKT_LIST *plp)
58 {
59 	boolean_t	result, existing_offer = B_FALSE;
60 	int		err, write_error = DSVC_SUCCESS, flags = 0;
61 	int		pkt_len;
62 	uint_t		crecords = 0, irecords = 0, srecords = 0, clen;
63 	uint32_t	query;
64 	PKT		*rep_pktp = NULL;
65 	IF		*ifp = pcd->ifp;
66 	dsvc_dnet_t	*pnd = pcd->pnd;
67 	uchar_t		*optp;
68 	dn_rec_t	dn, ndn, *dnp;
69 	dn_rec_list_t	*dncp = NULL, *dnip = NULL, *dnlp = NULL;
70 	struct in_addr	ciaddr;
71 	struct in_addr	no_ip;	/* network order IP */
72 	ENCODE		*ecp, *hecp;
73 	MACRO		*mp, *nmp, *cmp;
74 	time_t		now = time(NULL);
75 	DHCP_MSG_CATEGORIES	log;
76 	struct		hostent	h, *hp;
77 	char		ntoab[INET_ADDRSTRLEN], cipbuf[INET_ADDRSTRLEN];
78 	char		cidbuf[DHCP_MAX_OPT_SIZE];
79 	char		hbuf[NSS_BUFLEN_HOSTS];
80 
81 	ciaddr.s_addr = htonl(INADDR_ANY);
82 
83 #ifdef	DEBUG
84 	dhcpmsg(LOG_DEBUG, "BOOTP request received on %s\n", ifp->nm);
85 #endif	/* DEBUG */
86 
87 	if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
88 	    PCD_OFFER_TIMEOUT(pcd, now))
89 		purge_offer(pcd, B_TRUE, B_TRUE);
90 
91 	if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
92 		existing_offer = B_TRUE;
93 		dnlp = pcd->dnlp;
94 		assert(dnlp != NULL);
95 		dnp = dnlp->dnl_rec;
96 		no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
97 		crecords = 1;
98 	} else {
99 		/*
100 		 * Try to find a CID entry for the client. We don't care about
101 		 * lease info here, since a BOOTP client always has a permanent
102 		 * lease. We also don't care about the entry owner either,
103 		 * unless we end up allocating a new entry for the client.
104 		 */
105 		DSVC_QINIT(query);
106 
107 		DSVC_QEQ(query, DN_QCID);
108 		(void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
109 		dn.dn_cid_len = pcd->cid_len;
110 
111 		DSVC_QEQ(query, DN_QFBOOTP_ONLY);
112 		dn.dn_flags = DN_FBOOTP_ONLY;
113 
114 		/*
115 		 * If a client address (ciaddr) is given, we simply trust that
116 		 * the client knows what it's doing, and we use that IP address
117 		 * to locate the client's record. If we can't find the client's
118 		 * record, then we keep silent. If the client id of the record
119 		 * doesn't match this client, then either the client or our
120 		 * database is inconsistent, and we'll ignore it (notice
121 		 * message generated).
122 		 */
123 		ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
124 		if (ciaddr.s_addr != htonl(INADDR_ANY)) {
125 			DSVC_QEQ(query, DN_QCIP);
126 			dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
127 		}
128 
129 		dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
130 		    -1, &dn, (void **)&dncp, S_CID);
131 		if (dnlp != NULL) {
132 			crecords = 1;
133 			dnp = dnlp->dnl_rec;
134 			if (dnp->dn_flags & DN_FUNUSABLE)
135 				goto leave_bootp;
136 			no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
137 		}
138 	}
139 
140 	(void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
141 
142 	if (crecords == 0 && !be_automatic) {
143 		if (verbose) {
144 			dhcpmsg(LOG_INFO, "BOOTP client: %1$s is looking for "
145 			    "a configuration on net %2$s\n", pcd->cidbuf,
146 			    pnd->network);
147 		}
148 		goto leave_bootp;
149 	}
150 
151 	/*
152 	 * If the client thinks it knows who it is (ciaddr), and this doesn't
153 	 * match our registered IP address, then display an error message and
154 	 * give up.
155 	 */
156 	if (ciaddr.s_addr != htonl(INADDR_ANY) && crecords == 0) {
157 		/*
158 		 * If the client specified an IP address, then let's check
159 		 * whether it is available, since we have no CID mapping
160 		 * registered for this client. If it is available and
161 		 * unassigned but owned by a different server, we ignore the
162 		 * client.
163 		 */
164 		DSVC_QINIT(query);
165 
166 		DSVC_QEQ(query, DN_QCIP);
167 		dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
168 		(void) inet_ntop(AF_INET, &ciaddr, cipbuf, sizeof (cipbuf));
169 
170 		DSVC_QEQ(query, DN_QFBOOTP_ONLY);
171 		dn.dn_flags = DN_FBOOTP_ONLY;
172 
173 		dnip = NULL;
174 		dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
175 		    -1, &dn, (void **)&dncp, S_CID);
176 		if (dnlp == NULL) {
177 			/*
178 			 * We have no record of this client's IP address, thus
179 			 * we really can't respond to this client, because it
180 			 * doesn't have a configuration.
181 			 */
182 			if (verbose) {
183 				dhcpmsg(LOG_INFO, "No configuration for BOOTP "
184 				    "client: %1$s. IP address: %2$s not "
185 				    "administered by this server.\n",
186 				    pcd->cidbuf, inet_ntop(AF_INET, &ciaddr,
187 				    ntoab, sizeof (ntoab)));
188 			}
189 			goto leave_bootp;
190 		} else
191 			irecords = 1;
192 
193 		dnp = dnlp->dnl_rec;
194 		if (dnp->dn_flags & DN_FUNUSABLE)
195 			goto leave_bootp;
196 
197 		if (dn.dn_cid_len != 0) {
198 			if (dn.dn_cid_len != pcd->cid_len || memcmp(dn.dn_cid,
199 			    pcd->cid, pcd->cid_len) != 0) {
200 				if (verbose) {
201 					clen = sizeof (cidbuf);
202 					(void) octet_to_hexascii(dn.dn_cid,
203 					    dn.dn_cid_len, cidbuf, &clen);
204 					dhcpmsg(LOG_INFO, "BOOTP client: %1$s "
205 					    "thinks it owns %2$s, but that "
206 					    "address belongs to %3$s. Ignoring "
207 					    "client.\n", pcd->cidbuf, cipbuf,
208 					    cidbuf);
209 				}
210 				goto leave_bootp;
211 			}
212 		} else {
213 			if (match_ownerip(htonl(dn.dn_sip.s_addr)) == NULL) {
214 				if (verbose) {
215 					no_ip.s_addr =
216 					    htonl(dnp->dn_sip.s_addr);
217 					dhcpmsg(LOG_INFO, "BOOTP client: %1$s "
218 					    "believes it owns %2$s. That "
219 					    "address is free, but is owned by "
220 					    "DHCP server %3$s. Ignoring "
221 					    "client.\n", pcd->cidbuf, cipbuf,
222 					    inet_ntop(AF_INET, &no_ip, ntoab,
223 					    sizeof (ntoab)));
224 				}
225 				goto leave_bootp;
226 			}
227 		}
228 		no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
229 		(void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
230 	}
231 
232 	if (crecords == 0) {
233 		/*
234 		 * The dhcp-network table did not have any matching entries.
235 		 * Try to allocate a new one if possible.
236 		 */
237 		if (irecords == 0 && select_offer(pnd, plp, pcd, &dnlp)) {
238 			dnp = dnlp->dnl_rec;
239 			no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
240 			(void) inet_ntop(AF_INET, &no_ip, cipbuf,
241 			    sizeof (cipbuf));
242 			srecords = 1;
243 		}
244 	}
245 
246 	if (crecords == 0 && irecords == 0 && srecords == 0) {
247 		dhcpmsg(LOG_NOTICE,
248 		    "(%1$s) No more BOOTP IP addresses for %2$s network.\n",
249 		    pcd->cidbuf, pnd->network);
250 		goto leave_bootp;
251 	}
252 
253 	/* Check the address. But only if client doesn't know its address. */
254 	ndn = *dnp;	/* struct copy */
255 	no_ip.s_addr = htonl(ndn.dn_cip.s_addr);
256 	(void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
257 	if (ciaddr.s_addr == htonl(INADDR_ANY)) {
258 		if ((ifp->flags & IFF_NOARP) == 0)
259 			(void) set_arp(ifp, &no_ip, NULL, 0, DHCP_ARP_DEL);
260 		if (!noping) {
261 			/*
262 			 * If icmp echo check fails,
263 			 * let the plp fall by the wayside.
264 			 */
265 			errno = icmp_echo_check(&no_ip, &result);
266 			if (errno != 0) {
267 				dhcpmsg(LOG_ERR, "ICMP ECHO check cannot be "
268 				    "performed for: %s, ignoring\n", cipbuf);
269 				goto leave_bootp;
270 			}
271 			if (result) {
272 				dhcpmsg(LOG_ERR, "ICMP ECHO reply to BOOTP "
273 				    "OFFER candidate: %s, disabling.\n",
274 				    cipbuf);
275 
276 				ndn.dn_flags |= DN_FUNUSABLE;
277 
278 				if ((err = dhcp_modify_dd_entry(pnd->dh,
279 				    dnp, &ndn)) == DSVC_SUCCESS) {
280 					/* Keep the cached entry current. */
281 					*dnp = ndn;    /* struct copy */
282 				}
283 
284 				logtrans(P_BOOTP, L_ICMP_ECHO, 0, no_ip,
285 				    server_ip, plp);
286 
287 				goto leave_bootp;
288 			}
289 		}
290 	}
291 
292 	/*
293 	 * It is possible that the client could specify a REQUEST list,
294 	 * but then it would be a DHCP client, wouldn't it? Only copy the
295 	 * std option list, since that potentially could be changed by
296 	 * load_options().
297 	 */
298 	ecp = NULL;
299 	if (!no_dhcptab) {
300 		open_macros();
301 		if ((nmp = get_macro(pnd->network)) != NULL)
302 			ecp = dup_encode_list(nmp->head);
303 		if ((mp = get_macro(dnp->dn_macro)) != NULL)
304 			ecp = combine_encodes(ecp, mp->head, ENC_DONT_COPY);
305 		if ((cmp = get_macro(pcd->cidbuf)) != NULL)
306 			ecp = combine_encodes(ecp, cmp->head, ENC_DONT_COPY);
307 
308 		/* If dhcptab configured to return hostname, do so. */
309 		if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) !=
310 		    NULL) {
311 			hp = gethostbyaddr_r((char *)&ndn.dn_cip,
312 			    sizeof (struct in_addr), AF_INET, &h, hbuf,
313 			    sizeof (hbuf), &err);
314 			if (hp != NULL) {
315 				hecp = make_encode(DSYM_STANDARD,
316 				    CD_HOSTNAME, strlen(hp->h_name),
317 				    hp->h_name, ENC_COPY);
318 				replace_encode(&ecp, hecp, ENC_DONT_COPY);
319 			}
320 		}
321 	}
322 
323 	/* Produce a BOOTP reply. */
324 	rep_pktp = gen_bootp_pkt(sizeof (PKT), plp->pkt);
325 
326 	rep_pktp->op = BOOTREPLY;
327 	optp = rep_pktp->options;
328 
329 	/*
330 	 * Set the client's "your" IP address if client doesn't know it,
331 	 * otherwise echo the client's ciaddr back to him.
332 	 */
333 	if (ciaddr.s_addr == htonl(INADDR_ANY))
334 		rep_pktp->yiaddr.s_addr = htonl(ndn.dn_cip.s_addr);
335 	else
336 		rep_pktp->ciaddr.s_addr = ciaddr.s_addr;
337 
338 	/*
339 	 * Omit lease time options implicitly, e.g.
340 	 * ~(DHCP_DHCP_CLNT | DHCP_SEND_LEASE)
341 	 */
342 
343 	if (!plp->rfc1048)
344 		flags |= DHCP_NON_RFC1048;
345 
346 	/* Now load in configured options. */
347 	pkt_len = load_options(flags, plp, rep_pktp, sizeof (PKT), optp, ecp,
348 	    NULL);
349 
350 	free_encode_list(ecp);
351 	if (!no_dhcptab)
352 		close_macros();
353 
354 	if (pkt_len < sizeof (PKT))
355 		pkt_len = sizeof (PKT);
356 
357 	/*
358 	 * Only perform a write if we have selected an entry not yet
359 	 * assigned to the client (a matching DN_FBOOTP_ONLY entry from
360 	 * ip address lookup, or an unassigned entry from select_offer()).
361 	 */
362 	if (srecords > 0 || irecords > 0) {
363 		(void) memcpy(&ndn.dn_cid, pcd->cid, pcd->cid_len);
364 		ndn.dn_cid_len = pcd->cid_len;
365 
366 		write_error = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
367 
368 		/* Keep state of the cached entry current. */
369 		*dnp = ndn;	/* struct copy */
370 
371 		log = L_ASSIGN;
372 	} else {
373 		if (verbose) {
374 			dhcpmsg(LOG_INFO, "Database write unnecessary for "
375 			    "BOOTP client: %1$s, %2$s\n",
376 			    pcd->cidbuf, cipbuf);
377 		}
378 		log = L_REPLY;
379 	}
380 
381 	if (write_error == DSVC_SUCCESS) {
382 		if (send_reply(ifp, rep_pktp, pkt_len, &no_ip) != 0) {
383 			dhcpmsg(LOG_ERR,
384 			    "Reply to BOOTP client %1$s with %2$s failed.\n",
385 			    pcd->cidbuf, cipbuf);
386 		} else {
387 			/* Note that the conversation has completed. */
388 			pcd->state = ACK;
389 
390 			(void) update_offer(pcd, &dnlp, 0, &no_ip, B_TRUE);
391 			existing_offer = B_TRUE;
392 		}
393 
394 		logtrans(P_BOOTP, log, ndn.dn_lease, no_ip, server_ip, plp);
395 	}
396 
397 leave_bootp:
398 	if (rep_pktp != NULL)
399 		free(rep_pktp);
400 	if (dncp != NULL)
401 		dhcp_free_dd_list(pnd->dh, dncp);
402 	if (dnip != NULL)
403 		dhcp_free_dd_list(pnd->dh, dnip);
404 	if (dnlp != NULL && !existing_offer)
405 		dhcp_free_dd_list(pnd->dh, dnlp);
406 }
407