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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * This file contains routines that are shared between the DHCP server
29 * implementation and BOOTP server compatibility.
30 */
31
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <assert.h>
37 #include <sys/types.h>
38 #include <stdarg.h>
39 #include <errno.h>
40 #include <alloca.h>
41 #include <sys/socket.h>
42 #include <sys/sockio.h>
43 #include <net/if.h>
44 #include <sys/syslog.h>
45 #include <string.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <netdb.h>
49 #include <netinet/dhcp.h>
50 #include <search.h>
51 #include <dhcp_symbol.h>
52 #include "dhcpd.h"
53 #include "per_dnet.h"
54 #include "interfaces.h"
55 #include <locale.h>
56 #include <resolv.h>
57
58 /*
59 * Get the client id. Sets cid and len.
60 */
61 void
62 get_clnt_id(PKT_LIST *plp, uchar_t *cid, int cidlen, uchar_t *len)
63 {
64 DHCP_OPT *optp = plp->opts[CD_CLIENT_ID];
65
66 /*
67 * If the DHCP client specified the client id option, use that,
68 * otherwise use the client's hardware type and hardware address.
69 */
70 if (plp->opts[CD_DHCP_TYPE] != NULL && optp != NULL) {
71 /* DHCP client w/ client id */
72 if (cidlen < optp->len)
73 *len = (uchar_t)cidlen;
74 else
75 *len = optp->len;
76 (void) memcpy(cid, optp->value, *len);
77 } else {
78 /* BOOTP client or DHCP client w/o client id. */
79 *cid++ = plp->pkt->htype;
80 *len = plp->pkt->hlen + 1;
81 if (cidlen < *len)
82 *len = cidlen;
83 (void) memcpy(cid, plp->pkt->chaddr, *len);
84 }
85 }
86
87 /*
88 * Return a string representing an ASCII version of the client_id.
89 */
90 char *
91 disp_cid(PKT_LIST *plp, char *bufp, int len)
92 {
93 DHCP_OPT *optp = plp->opts[CD_CLIENT_ID];
94 uchar_t *cp;
95 uchar_t cplen;
96 uint_t tlen;
97
98 if (optp != (DHCP_OPT *)0) {
99 cp = optp->value;
100 cplen = optp->len;
101 } else {
102 cp = plp->pkt->chaddr;
103 cplen = plp->pkt->hlen;
104 }
105
106 tlen = len;
107 (void) octet_to_hexascii(cp, cplen, bufp, &tlen);
108 return (bufp);
109 }
110
111 /*
112 * Based on the contents of the PKT_LIST structure for an incoming
113 * packet, determine the net address and subnet mask identifying the
114 * dhcp-network database. This centralizes choices that were formerly
115 * made in the specific protocol routines.
116 */
117 void
118 determine_network(IF *ifp, PKT_LIST *plp, struct in_addr *netp,
119 struct in_addr *subp)
120 {
121 /*
122 * For BOOTP, REQUEST, RELEASE, and INFORM packets, trust client's
123 * notion of IP address if ciaddr is set. Use it to figure out correct
124 * dhcp-network.
125 */
126 netp->s_addr = plp->pkt->ciaddr.s_addr;
127 if (netp->s_addr != htonl(INADDR_ANY) &&
128 (plp->opts[CD_DHCP_TYPE] == NULL ||
129 (*plp->opts[CD_DHCP_TYPE]->value == REQUEST ||
130 *plp->opts[CD_DHCP_TYPE]->value == RELEASE ||
131 *plp->opts[CD_DHCP_TYPE]->value == INFORM))) {
132 /*
133 * Calculate client's default net mask, consult netmasks
134 * database to see if net is further subnetted. Use resulting
135 * subnet mask with client's address to produce dhcp-network
136 * database name.
137 */
138 get_netmask(netp, subp);
139 } else
140 netp->s_addr = htonl(INADDR_ANY);
141
142 /*
143 * If no trusted IP address, examine giaddr.
144 */
145 if (netp->s_addr == htonl(INADDR_ANY)) {
146 if (plp->pkt->giaddr.s_addr != htonl(INADDR_ANY)) {
147 netp->s_addr = plp->pkt->giaddr.s_addr;
148 /*
149 * Packet received thru a relay agent. Calculate the
150 * net's address using subnet mask and giaddr.
151 */
152 get_netmask(netp, subp);
153 } else {
154 /* Locally connected net. */
155 netp->s_addr = ifp->addr.s_addr;
156 subp->s_addr = ifp->mask.s_addr;
157 }
158 }
159 }
160
161 struct netmask_node;
162
163 typedef struct netmask_node {
164 struct in_addr net; /* cached network */
165 struct in_addr mask; /* cached netmask */
166 } NNODE;
167
168 static void *nroot; /* root of netmask tree */
169 static time_t nroot_mtime; /* time for dynamic free */
170 static time_t nroot_stamp; /* time for dynamic free */
171 static rwlock_t nroot_rwlock; /* synchronization variable */
172
173 /*
174 * nm_cmp() - determine whether key n1 is within range of net/mask n2
175 */
176 static int
177 nm_cmp(const void *n1, const void *n2)
178 {
179 void *v1 = (void *) (((NNODE *)n1)->net.s_addr &
180 ((NNODE *)n2)->mask.s_addr);
181 void *v2 = (void *) ((NNODE *)n2)->net.s_addr;
182
183 return (memcmp(&v1, &v2, sizeof (struct in_addr)));
184 }
185
186 /*
187 * Given a network-order address, calculate client's default net mask.
188 * Consult local cache, then netmasks database to see if net is further
189 * subnetted. We'll only snag the first netmask that matches our criteria.
190 */
191 void
192 get_netmask(struct in_addr *n_addrp, struct in_addr *s_addrp)
193 {
194 NNODE key;
195 NNODE *node;
196 NNODE **ret;
197 struct in_addr haddr;
198
199 assert(n_addrp != NULL && s_addrp != NULL);
200
201 /*
202 * First check locally maintained, incomplete cache.
203 */
204 (void) rw_rdlock(&nroot_rwlock);
205 if (nroot != NULL) {
206 /* Delete expired tree. */
207 if (nroot_mtime != reinit_time || nroot_stamp < time(NULL)) {
208 (void) rw_unlock(&nroot_rwlock);
209 (void) rw_wrlock(&nroot_rwlock);
210 while ((ret = (NNODE **)nroot) != NULL) {
211 node = *ret;
212 (void) tdelete(node, &nroot, nm_cmp);
213 free(node);
214 }
215 nroot_mtime = reinit_time;
216 nroot_stamp = time(NULL) + DHCP_NSS_TIME;
217 } else {
218 key.net.s_addr = ntohl(n_addrp->s_addr);
219 key.mask.s_addr = INADDR_ANY;
220 if ((ret = (NNODE **)tfind((void *)&key,
221 (void * const *)&nroot, nm_cmp)) != NULL) {
222 s_addrp->s_addr = htonl((*ret)->mask.s_addr);
223 (void) rw_unlock(&nroot_rwlock);
224 return;
225 }
226 }
227 }
228
229 /*
230 * Note: workaround for 4336124: single-thread access to
231 * nss search routines to avoid getting incorrect results.
232 */
233 node = (NNODE *)smalloc(sizeof (NNODE));
234
235 /* Convert to and from host order. */
236 haddr.s_addr = ntohl(n_addrp->s_addr);
237 get_netmask4(&haddr, s_addrp);
238 node->mask.s_addr = s_addrp->s_addr;
239 node->net.s_addr = haddr.s_addr & node->mask.s_addr;
240 s_addrp->s_addr = htonl(s_addrp->s_addr);
241
242 /* While inserting check that another insert has not occurred. */
243 ret = (NNODE **)tsearch((void *)node, &nroot, nm_cmp);
244 if (ret != NULL && *ret != node)
245 free(node);
246
247 (void) rw_unlock(&nroot_rwlock);
248 }
249
250 /*
251 * This function is charged with loading the options field with the
252 * configured and/or asked for options. Note that if the packet is too
253 * small to fit the options, then option overload is enabled.
254 *
255 * Note that the caller is expected to free any allocated ENCODE lists,
256 * with the exception of locally-allocated lists in the case where ecp is
257 * NULL, but vecp is not. In this case, the resultant ecp list (ecp == tvep)
258 * is freed locally.
259 *
260 * Returns: The actual size of the utilized packet buffer.
261 */
262
263 int
264 load_options(int flags, PKT_LIST *c_plp, PKT *r_pktp, int replen, uchar_t *optp,
265 ENCODE *ecp, ENCODE *vecp)
266 {
267 ENCODE *ep, *prevep, *tvep = NULL;
268 ENCODE *router_ecp = NULL;
269 PKT *c_pktp = c_plp->pkt;
270 uchar_t cat;
271 ushort_t code;
272 uint_t vend_len;
273 uchar_t len, *vp, *vdata, *data, *endp, *main_optp, *opt_endp;
274 uchar_t overload = DHCP_OVRLD_CLR;
275 uchar_t using_overload = DHCP_OVRLD_CLR;
276 boolean_t srv_using_file = B_FALSE, clnt_ovrld_file = B_FALSE;
277 boolean_t echo_clnt_file;
278
279 if (c_plp->opts[CD_OPTION_OVERLOAD] != NULL &&
280 *c_plp->opts[CD_OPTION_OVERLOAD]->value & DHCP_OVRLD_FILE)
281 clnt_ovrld_file = B_TRUE;
282
283 opt_endp = (uchar_t *)((uint_t)r_pktp->options + replen -
284 BASE_PKT_SIZE);
285 endp = opt_endp;
286
287 /*
288 * We handle vendor options by fabricating an ENCODE of type
289 * CD_VENDOR_SPEC, and setting its datafield equal to vecp.
290 *
291 * We assume we've been handed the proper class list.
292 */
293 if (vecp != NULL && (flags & DHCP_NON_RFC1048) == 0) {
294 vend_len = 0;
295 for (ep = vecp, vend_len = 0; ep != NULL; ep = ep->next)
296 vend_len += (ep->len + 2);
297
298 if (vend_len != 0) {
299 if (vend_len > (uint_t)0xff) {
300 dhcpmsg(LOG_WARNING,
301 "Warning: Too much vendor data (> 255) to "
302 "encapsulate within option %d.\n",
303 CD_VENDOR_SPEC);
304 vend_len = (uint_t)0xff;
305 }
306 vdata = (uchar_t *)smalloc(vend_len);
307
308 for (vp = vdata, tvep = vecp; tvep != NULL &&
309 (uchar_t *)(vp + tvep->len + 2) <= &vdata[vend_len];
310 tvep = tvep->next) {
311 *vp++ = tvep->code;
312 *vp++ = tvep->len;
313 (void) memcpy(vp, tvep->data, tvep->len);
314 vp += tvep->len;
315 }
316
317 /* this make_encode *doesn't* copy data */
318 tvep = make_encode(DSYM_VENDOR, CD_VENDOR_SPEC,
319 vend_len, vdata, ENC_DONT_COPY);
320
321 /* Tack it on the end of standard list. */
322 for (ep = prevep = ecp; ep != NULL; ep = ep->next)
323 prevep = ep;
324 if (prevep != NULL)
325 prevep->next = tvep;
326 else
327 ecp = tvep;
328 }
329 }
330
331 /*
332 * Scan the options first to determine if we could potentially
333 * option overload.
334 */
335 if (flags & DHCP_DHCP_CLNT) {
336 for (ep = ecp; ep != NULL; ep = ep->next) {
337 if (ep->category == DSYM_FIELD)
338 switch (ep->code) {
339 case CD_SNAME:
340 overload |= DHCP_OVRLD_SNAME;
341 break;
342 case CD_BOOTFILE:
343 overload |= DHCP_OVRLD_FILE;
344 srv_using_file = B_TRUE;
345 break;
346 }
347 }
348 } else {
349 /* BOOTP uses these fields for fixed parameters, no overload */
350 overload = DHCP_OVRLD_ALL;
351 }
352
353 if (c_pktp->file[0] != '\0' && !clnt_ovrld_file && !srv_using_file) {
354 /*
355 * simply echo back client's boot file, and don't overload.
356 * if CD_BOOTPATH is set, we'll simply rewrite the r_pktp
357 * file field to include it along with the client's requested
358 * name during the load pass through the internal options.
359 * Here we let the overload code know we're not to overload
360 * the file field.
361 */
362 (void) memcpy(r_pktp->file, c_pktp->file,
363 sizeof (r_pktp->file));
364 overload |= DHCP_OVRLD_FILE;
365 echo_clnt_file = B_TRUE;
366 } else
367 echo_clnt_file = B_FALSE;
368
369 /* Now actually load the options! */
370 for (ep = ecp; ep != NULL; ep = ep->next) {
371 cat = ep->category;
372 code = ep->code;
373 len = ep->len;
374 data = ep->data;
375
376 /*
377 * non rfc1048 clients can only get packet fields and
378 * the CD_BOOTPATH internal pseudo opt, which only potentially
379 * affects the file field.
380 */
381 if ((flags & DHCP_NON_RFC1048) &&
382 !(cat == DSYM_FIELD || (cat == DSYM_INTERNAL &&
383 code == CD_BOOTPATH))) {
384 continue;
385 }
386
387 if ((flags & DHCP_SEND_LEASE) == 0 &&
388 cat == DSYM_STANDARD &&
389 (code == CD_T1_TIME || code == CD_T2_TIME ||
390 code == CD_LEASE_TIME)) {
391 continue;
392 }
393
394 /* standard and site options */
395 if (cat == DSYM_STANDARD || cat == DSYM_SITE ||
396 cat == DSYM_VENDOR) {
397
398 uchar_t *need_optp;
399
400 /*
401 * This horrible kludge is necessary because the DHCP
402 * options RFCs require that the subnet option MUST
403 * precede the router option. To accomplish this, we
404 *
405 * inspect each of the standard options, waiting
406 * for CD_ROUTER to turn up (if it never does,
407 * no special handling is needed)
408 *
409 * search the remaining options for CD_SUBNETMASK
410 * If it occurs, we
411 * set router_ecp to indicate where to find
412 * the router option's values that we have
413 * not yet emitted
414 *
415 * reinitialize code, len, and data to emit
416 * the CD_SUBNETMASK option now
417 *
418 * when CD_SUBNETMASK is encountered, we
419 * reinitialize code, len, and data to emit
420 * the CD_ROUTER option
421 */
422 if ((cat == DSYM_STANDARD) && (code == CD_ROUTER)) {
423 ENCODE *tp;
424
425 for (tp = ep->next; tp != NULL; tp = tp->next)
426 if ((tp->category == DSYM_STANDARD) &&
427 (tp->code == CD_SUBNETMASK)) {
428 router_ecp = ep;
429 code = CD_SUBNETMASK;
430 len = tp->len;
431 data = tp->data;
432 }
433 } else if ((cat == DSYM_STANDARD) &&
434 (code == CD_SUBNETMASK) && (router_ecp != NULL)) {
435 code = CD_ROUTER;
436 len = router_ecp->len;
437 data = router_ecp->data;
438 }
439
440 /*
441 * Keep an eye on option field. Option overload. Note
442 * that we need to keep track of the space necessary
443 * to place the Overload option in the options section
444 * (that's the 3 octets below.) The 2 octets cover the
445 * necessary code and len portion of the payload.
446 */
447 if (using_overload == DHCP_OVRLD_CLR) {
448 /* 2 for code/len, 3 for overload option */
449 need_optp = &optp[len + 2 + 3];
450 } else {
451 /* Just need 2 for code/len */
452 need_optp = &optp[len + 2];
453 }
454 if (need_optp > endp) {
455 /*
456 * If overload is not possible, we will
457 * keep going, hoping to find an option
458 * that will fit in the remaining space,
459 * rather than just give up.
460 */
461 if (overload != DHCP_OVRLD_ALL) {
462 if (using_overload == DHCP_OVRLD_CLR) {
463 *optp++ = CD_OPTION_OVERLOAD;
464 *optp++ = 1;
465 main_optp = optp;
466 } else {
467 if (optp < endp)
468 *optp = CD_END;
469 overload |= using_overload;
470 }
471 }
472 switch (overload) {
473 case DHCP_OVRLD_CLR:
474 /* great, can use both */
475 /* FALLTHRU */
476 case DHCP_OVRLD_FILE:
477 /* Can use sname. */
478 optp = r_pktp->sname;
479 endp = r_pktp->file;
480 using_overload |= DHCP_OVRLD_SNAME;
481 break;
482 case DHCP_OVRLD_SNAME:
483 /* Using sname, can use file. */
484 optp = r_pktp->file;
485 endp = r_pktp->cookie;
486 using_overload |= DHCP_OVRLD_FILE;
487 break;
488 }
489 }
490 /* Skip the option if it's too long to fit */
491 if (len < (endp - optp - 1)) {
492 /* Load options. */
493 *optp++ = (uchar_t)code;
494 *optp++ = len;
495 (void) memcpy(optp, data, len);
496 optp += len;
497 }
498 } else if (cat == DSYM_FIELD) {
499 /* packet field pseudo options */
500 switch (code) {
501 case CD_SIADDR:
502 /*
503 * Configuration includes Boot server addr
504 */
505 (void) memcpy((void *)&r_pktp->siaddr, data,
506 len);
507 break;
508 case CD_SNAME:
509 /*
510 * Configuration includes Boot server name
511 */
512 (void) memcpy(r_pktp->sname, data, len);
513 break;
514 case CD_BOOTFILE:
515 /*
516 * Configuration includes boot file.
517 * Always authoritative.
518 */
519 (void) memset(r_pktp->file, 0,
520 sizeof (r_pktp->file));
521 (void) memcpy(r_pktp->file, data, len);
522 break;
523 default:
524 dhcpmsg(LOG_ERR,
525 "Unsettable DHCP packet field: %d\n", code);
526 break;
527 }
528 } else if (cat == DSYM_INTERNAL) {
529 /* Internal server pseudo options */
530 switch (code) {
531 case CD_BOOTPATH:
532 /*
533 * Prefix for boot file. Only used if
534 * client provides bootfile and server doesn't
535 * specify one. Prepended on client's bootfile
536 * value. Otherwise ignored.
537 */
538 if (echo_clnt_file) {
539 uchar_t alen, flen;
540
541 alen = sizeof (c_pktp->file);
542 flen = alen - 1;
543 if (c_pktp->file[flen] != '\0')
544 flen++;
545 else
546 flen = strlen(
547 (char *)c_pktp->file);
548
549 if ((len + flen + 1) > alen) {
550 char *bp = alloca(alen + 1);
551 char *bf = alloca(alen + 1);
552 (void) memcpy(bp, data, len);
553 bp[len] = '\0';
554 (void) memcpy(bf, c_pktp->file,
555 flen);
556 bf[flen] = '\0';
557 dhcpmsg(LOG_ERR,
558 "BootPath(%1$s) + "
559 "BootFile(%2$s) too "
560 "long: %3$d > %4$d\n",
561 bp, bf, (len + flen), alen);
562 } else {
563 (void) memcpy(r_pktp->file,
564 data, len);
565 r_pktp->file[len] = '/';
566 (void) memcpy(
567 &r_pktp->file[len + 1],
568 c_pktp->file, flen);
569 }
570 }
571 break;
572 case CD_BOOL_HOSTNAME:
573 /* FALLTHRU */
574 case CD_BOOL_LEASENEG:
575 /* FALLTHRU */
576 case CD_BOOL_ECHO_VCLASS:
577 /*
578 * These pseudo opts have had their
579 * affect elsewhere, such as dhcp.c.
580 */
581 break;
582 default:
583 dhcpmsg(LOG_ERR,
584 "Unknown Internal pseudo opt: %d\n", code);
585 break;
586 }
587 } else {
588 dhcpmsg(LOG_ERR,
589 "Unrecognized option with code: %d %d\n", cat,
590 code);
591 }
592 }
593
594 if (using_overload != DHCP_OVRLD_CLR) {
595 *main_optp++ = using_overload;
596 if (optp < endp)
597 *optp = CD_END;
598 } else
599 main_optp = optp; /* no overload */
600
601 if (main_optp < opt_endp)
602 *main_optp++ = CD_END;
603
604 if (ecp == tvep)
605 free_encode_list(ecp);
606
607 return (BASE_PKT_SIZE + (uint_t)(main_optp - r_pktp->options));
608 }
609
610 /*
611 * Reinitialize the dhcptab database, as a result of timeout or
612 * user signal. Note: if_head_mtx cannot be held by caller.
613 */
614 void *
615 reinitialize(void *arg)
616 {
617 int totpkts;
618 IF *ifp;
619 thread_t *tp = (thread_t *)arg;
620 int err;
621
622 /*
623 * Got a signal to reinitialize
624 */
625
626 if (verbose)
627 dhcpmsg(LOG_INFO, "Reinitializing server\n");
628
629 if (!no_dhcptab) {
630 if (checktab() != 0) {
631 dhcpmsg(LOG_WARNING,
632 "WARNING: Cannot access dhcptab.\n");
633 } else {
634 if ((err = readtab(PRESERVE_DHCPTAB)) != 0) {
635 dhcpmsg(LOG_ERR,
636 "Error reading dhcptab.\n");
637 return ((void *)err);
638 }
639 }
640 }
641
642 /*
643 * Drop all pending offers, display interface statistics.
644 */
645 if (verbose) {
646 (void) mutex_lock(&if_head_mtx);
647 for (ifp = if_head, totpkts = 0; ifp != NULL; ifp = ifp->next) {
648 (void) mutex_lock(&ifp->ifp_mtx);
649 disp_if_stats(ifp);
650 totpkts += ifp->received;
651 (void) mutex_unlock(&ifp->ifp_mtx);
652 }
653 (void) mutex_unlock(&if_head_mtx);
654
655 dhcpmsg(LOG_INFO,
656 "Total Packets received on all interfaces: %d\n", totpkts);
657 dhcpmsg(LOG_INFO, "Server reinitialized.\n");
658 }
659
660 /* Default domain may have changed */
661 if (res_ninit(&resolv_conf) == -1)
662 dhcpmsg(LOG_ERR, "Cannot acquire resolver configuration.\n");
663
664 /* Release reinitialization thread */
665 reinit_time = time(NULL);
666 *tp = NULL;
667 thr_exit(NULL);
668
669 return (NULL);
670 }