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 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 }