xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c (revision 20f2cb7bd9b5c74d65bc6f7d24fc0018f2f6bde7)
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 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/sysmacros.h>
35 #include <netinet/in.h>
36 #include <netinet/dhcp.h>
37 #include <arpa/inet.h>
38 #include <dhcp_inittab.h>
39 #include <dhcp_symbol.h>
40 #include "snoop.h"
41 
42 static const char *show_msgtype(unsigned char);
43 static int show_options(unsigned char *, int);
44 static void display_ip(int, char *, char *, unsigned char **);
45 static void display_ascii(char *, char *, unsigned char **);
46 static void display_number(char *, char *, unsigned char **);
47 static void display_ascii_hex(char *, unsigned char **);
48 static unsigned char bootmagic[] = BOOTMAGIC;	/* rfc 1048 */
49 
50 static char *option_types[] = {
51 "",					/* 0 */
52 "Subnet Mask",				/* 1 */
53 "UTC Time Offset",			/* 2 */
54 "Router",				/* 3 */
55 "RFC868 Time Servers",			/* 4 */
56 "IEN 116 Name Servers",			/* 5 */
57 "DNS Servers",				/* 6 */
58 "UDP LOG Servers",			/* 7 */
59 "RFC 865 Cookie Servers",		/* 8 */
60 "RFC 1179 Line Printer Servers (LPR)",	/* 9 */
61 "Impress Servers",			/* 10 */
62 "RFC 887 Resource Location Servers",	/* 11 */
63 "Client Hostname",			/* 12 */
64 "Boot File size in 512 byte Blocks",	/* 13 */
65 "Merit Dump File",			/* 14 */
66 "DNS Domain Name",			/* 15 */
67 "SWAP Server",				/* 16 */
68 "Client Root Path",			/* 17 */
69 "BOOTP options extensions path",	/* 18 */
70 "IP Forwarding Flag",			/* 19 */
71 "NonLocal Source Routing Flag",		/* 20 */
72 "Policy Filters for NonLocal Routing",	/* 21 */
73 "Maximum Datagram Reassembly Size",	/* 22 */
74 "Default IP Time To Live",		/* 23 */
75 "Path MTU Aging Timeout",		/* 24 */
76 "Path MTU Size Plateau Table",		/* 25 */
77 "Interface MTU Size",			/* 26 */
78 "All Subnets are Local Flag",		/* 27 */
79 "Broadcast Address",			/* 28 */
80 "Perform Mask Discovery Flag",		/* 29 */
81 "Mask Supplier Flag",			/* 30 */
82 "Perform Router Discovery Flag",	/* 31 */
83 "Router Solicitation Address",		/* 32 */
84 "Static Routes",			/* 33 */
85 "Trailer Encapsulation Flag",		/* 34 */
86 "ARP Cache Timeout Seconds",		/* 35 */
87 "Ethernet Encapsulation Flag",		/* 36 */
88 "TCP Default Time To Live",		/* 37 */
89 "TCP Keepalive Interval Seconds",	/* 38 */
90 "TCP Keepalive Garbage Flag",		/* 39 */
91 "NIS Domainname",			/* 40 */
92 "NIS Servers",				/* 41 */
93 "Network Time Protocol Servers",	/* 42 */
94 "Vendor Specific Options",		/* 43 */
95 "NetBIOS RFC 1001/1002 Name Servers",	/* 44 */
96 "NetBIOS Datagram Dist. Servers",	/* 45 */
97 "NetBIOS Node Type",			/* 46 */
98 "NetBIOS Scope",			/* 47 */
99 "X Window Font Servers",		/* 48 */
100 "X Window Display Manager Servers",	/* 49 */
101 "Requested IP Address",			/* 50 */
102 "IP Address Lease Time",		/* 51 */
103 "Option Field Overload Flag",		/* 52 */
104 "DHCP Message Type",			/* 53 */
105 "DHCP Server Identifier",		/* 54 */
106 "Option Request List",			/* 55 */
107 "Error Message",			/* 56 */
108 "Maximum DHCP Message Size",		/* 57 */
109 "Renewal (T1) Time Value",		/* 58 */
110 "Rebinding (T2) Time Value",		/* 59 */
111 "Client Class Identifier =",		/* 60 */
112 "Client Identifier =",			/* 61 */
113 "Netware IP Domain =",			/* 62 */
114 "Netware IP Options =",			/* 63 */
115 "NISPLUS Domainname",			/* 64 */
116 "NISPLUS Servers",			/* 65 */
117 "TFTP Server Name",			/* 66 */
118 "Option BootFile Name",			/* 67 */
119 "Mobile IP Agents",			/* 68 */
120 "Simple Mail (SMTP) Servers",		/* 69 */
121 "Post Office (POP3) Servers",		/* 70 */
122 "Net News (NNTP) Servers",		/* 71 */
123 "WorldWideWeb Servers",			/* 72 */
124 "Finger Servers",			/* 73 */
125 "Internet Relay Chat (IRC) Servers",	/* 74 */
126 "StreetTalk Servers",			/* 75 */
127 "StreetTalk Directory Assist. Servers",	/* 76 */
128 "User Class Identifier",		/* 77 */
129 };
130 
131 #define	OPTIONS_ARRAY_SIZE	78
132 
133 int
134 interpret_dhcp(int flags, struct dhcp *dp, int len)
135 {
136 	if (flags & F_SUM) {
137 		if ((memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) == 0) &&
138 		    (len >= BASE_PKT_SIZE + 3) &&
139 		    dp->options[0] == CD_DHCP_TYPE) {
140 			(void) sprintf(get_sum_line(),
141 			    "DHCP/BOOTP %s", show_msgtype(dp->options[2]));
142 		} else {
143 			switch (ntohs(dp->op)) {
144 			case BOOTREQUEST:
145 				(void) sprintf(get_sum_line(),
146 				    "DHCP/BOOTP BOOTREQUEST");
147 				break;
148 			case BOOTREPLY:
149 				(void) sprintf(get_sum_line(),
150 				    "DHCP/BOOTP BOOTREPLY");
151 				break;
152 			}
153 		}
154 	}
155 	if (flags & F_DTAIL) {
156 		show_header("DHCP: ", "Dynamic Host Configuration Protocol",
157 		    len);
158 		show_space();
159 		(void) sprintf(get_line((char *)(uintptr_t)dp->htype -
160 		    dlc_header, 1),
161 		    "Hardware address type (htype) =  %d (%s)", dp->htype,
162 		    arp_htype(dp->htype));
163 		(void) sprintf(get_line((char *)(uintptr_t)dp->hlen -
164 		    dlc_header, 1),
165 		    "Hardware address length (hlen) = %d octets", dp->hlen);
166 		(void) sprintf(get_line((char *)(uintptr_t)dp->hops -
167 		    dlc_header, 1),
168 		    "Relay agent hops = %d", dp->hops);
169 		(void) sprintf(get_line((char *)(uintptr_t)dp->xid -
170 		    dlc_header, 4),
171 		    "Transaction ID = 0x%x", ntohl(dp->xid));
172 		(void) sprintf(get_line((char *)(uintptr_t)dp->secs -
173 		    dlc_header, 2),
174 		    "Time since boot = %d seconds", ntohs(dp->secs));
175 		(void) sprintf(get_line((char *)(uintptr_t)dp->flags -
176 		    dlc_header, 2),
177 		    "Flags = 0x%.4x", ntohs(dp->flags));
178 		(void) sprintf(get_line((char *)&dp->ciaddr - dlc_header, 4),
179 		    "Client address (ciaddr) = %s", inet_ntoa(dp->ciaddr));
180 		(void) sprintf(get_line((char *)&dp->yiaddr - dlc_header, 4),
181 		    "Your client address (yiaddr) = %s",
182 		    inet_ntoa(dp->yiaddr));
183 		(void) sprintf(get_line((char *)&dp->siaddr - dlc_header, 4),
184 		    "Next server address (siaddr) = %s",
185 		    inet_ntoa(dp->siaddr));
186 		(void) sprintf(get_line((char *)&dp->giaddr - dlc_header, 4),
187 		    "Relay agent address (giaddr) = %s",
188 		    inet_ntoa(dp->giaddr));
189 		if (dp->htype == 1) {
190 			(void) sprintf(get_line((char *)dp->chaddr -
191 			    dlc_header, dp->hlen),
192 	"Client hardware address (chaddr) = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
193 			    dp->chaddr[0],
194 			    dp->chaddr[1],
195 			    dp->chaddr[2],
196 			    dp->chaddr[3],
197 			    dp->chaddr[4],
198 			    dp->chaddr[5]);
199 		}
200 		/*
201 		 * Check cookie, process options
202 		 */
203 		if (memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) != 0) {
204 			(void) sprintf(get_line(0, 0),
205 			    "Unrecognized cookie: 0x%.2X%.2X%.2X%.2X\n",
206 			    dp->cookie[0],
207 			    dp->cookie[1],
208 			    dp->cookie[2],
209 			    dp->cookie[3]);
210 			return (0);
211 		}
212 		show_space();
213 		show_header("DHCP: ", "(Options) field options", len);
214 		show_space();
215 		switch (show_options(dp->options, (len - BASE_PKT_SIZE))) {
216 		case 0:
217 			/* No option overloading */
218 			if (*(unsigned char *)(dp->sname) != '\0') {
219 				(void) sprintf(get_line(0, 0),
220 				    "Server Name = %s", dp->sname);
221 			}
222 			if (*(unsigned char *)(dp->file) != '\0') {
223 				(void) sprintf(get_line(0, 0),
224 				    "Boot File Name = %s", dp->file);
225 			}
226 			break;
227 		case 1:
228 			/* file field used */
229 			if (*(unsigned char *)(dp->sname) != '\0') {
230 				(void) sprintf(get_line(0, 0),
231 				    "Server Name = %s", dp->sname);
232 			}
233 			show_space();
234 			show_header("DHCP: ", "(File) field options", len);
235 			show_space();
236 			(void) show_options(dp->file, 128);
237 			break;
238 		case 2:
239 			/* sname field used for options */
240 			if (*(unsigned char *)(dp->file) != '\0') {
241 				(void) sprintf(get_line(0, 0),
242 				    "Boot File Name = %s", dp->file);
243 			}
244 			show_space();
245 			show_header("DHCP: ", "(Sname) field options", len);
246 			show_space();
247 			(void) show_options(dp->sname, 64);
248 			break;
249 		case 3:
250 			show_space();
251 			show_header("DHCP: ", "(File) field options", len);
252 			show_space();
253 			(void) show_options(dp->file, 128);
254 			show_space();
255 			show_header("DHCP: ", "(Sname) field options", len);
256 			show_space();
257 			(void) show_options(dp->sname, 64);
258 			break;
259 		};
260 	}
261 	return (len);
262 }
263 
264 static int
265 show_options(unsigned char  *cp, int len)
266 {
267 	char *prmpt;
268 	unsigned char *end, *vend;
269 	unsigned char *start, save;
270 	int items, i;
271 	int nooverload = 0;
272 	ushort_t	s_buf;
273 	struct in_addr	tmp;
274 	char scratch[128];
275 	dhcp_symbol_t *entry;
276 	char *decoded_opt;
277 	int opt_len;
278 
279 	start = cp;
280 	end = (unsigned char *)cp + len;
281 
282 	while (start < end) {
283 		if (*start == CD_PAD) {
284 			start++;
285 			continue;
286 		}
287 		if (*start == CD_END)
288 			break;	/* done */
289 
290 		save = *start++;
291 		switch (save) {
292 		/* Network order IP address(es) */
293 		case CD_SUBNETMASK:
294 		case CD_ROUTER_SOLICIT_SERV:
295 		case CD_BROADCASTADDR:
296 		case CD_REQUESTED_IP_ADDR:
297 		case CD_SERVER_ID:
298 			/* Single IP address */
299 			if (*start != 4) {
300 				(void) sprintf(get_line(0, 0),
301 				    "Error: Bad %s", option_types[save]);
302 			} else {
303 				start++;
304 				display_ip(1, "%s = %s", option_types[save],
305 				    &start);
306 			}
307 			break;
308 		case CD_ROUTER:
309 		case CD_TIMESERV:
310 		case CD_IEN116_NAME_SERV:
311 		case CD_DNSSERV:
312 		case CD_LOG_SERV:
313 		case CD_COOKIE_SERV:
314 		case CD_LPR_SERV:
315 		case CD_IMPRESS_SERV:
316 		case CD_RESOURCE_SERV:
317 		case CD_SWAP_SERV:
318 		case CD_NIS_SERV:
319 		case CD_NTP_SERV:
320 		case CD_NETBIOS_NAME_SERV:
321 		case CD_NETBIOS_DIST_SERV:
322 		case CD_XWIN_FONT_SERV:
323 		case CD_XWIN_DISP_SERV:
324 		case CD_NISPLUS_SERVS:
325 		case CD_MOBILE_IP_AGENT:
326 		case CD_SMTP_SERVS:
327 		case CD_POP3_SERVS:
328 		case CD_NNTP_SERVS:
329 		case CD_WWW_SERVS:
330 		case CD_FINGER_SERVS:
331 		case CD_IRC_SERVS:
332 		case CD_STREETTALK_SERVS:
333 		case CD_STREETTALK_DA_SERVS:
334 			/* Multiple IP addresses */
335 			if ((*start % 4) != 0) {
336 				(void) sprintf(get_line(0, 0),
337 				    "Error: Bad %s address",
338 				    option_types[save]);
339 			} else {
340 				items = *start++ / 4;
341 				display_ip(items, "%s at = %s",
342 				    option_types[save], &start);
343 			}
344 			break;
345 		case CD_TFTP_SERV_NAME:
346 		case CD_HOSTNAME:
347 		case CD_DUMP_FILE:
348 		case CD_DNSDOMAIN:
349 		case CD_ROOT_PATH:
350 		case CD_NIS_DOMAIN:
351 		case CD_NETBIOS_SCOPE:
352 		case CD_MESSAGE:
353 		case CD_NISPLUS_DMAIN:
354 		case CD_OPT_BOOTFILE_NAME:
355 		case CD_USER_CLASS_ID:
356 			/* Ascii strings */
357 			display_ascii("%s = %s", option_types[save], &start);
358 			break;
359 		case CD_TIMEOFFSET:
360 		case CD_IPTTL:
361 		case CD_PATH_MTU_TIMEOUT:
362 		case CD_ARP_TIMEOUT:
363 		case CD_TCP_TTL:
364 		case CD_TCP_KALIVE_INTVL:
365 		case CD_T1_TIME:
366 		case CD_T2_TIME:
367 		case CD_LEASE_TIME:
368 			/* Number: seconds */
369 			display_number("%s = %d seconds", option_types[save],
370 			    &start);
371 			break;
372 		case CD_IP_FORWARDING_ON:
373 		case CD_NON_LCL_ROUTE_ON:
374 		case CD_ALL_SUBNETS_LCL_ON:
375 		case CD_MASK_DISCVRY_ON:
376 		case CD_MASK_SUPPLIER_ON:
377 		case CD_ROUTER_DISCVRY_ON:
378 		case CD_TRAILER_ENCAPS_ON:
379 		case CD_ETHERNET_ENCAPS_ON:
380 		case CD_TCP_KALIVE_GRBG_ON:
381 			/* Number:  hex flag */
382 			display_number("%s flag = 0x%x", option_types[save],
383 			    &start);
384 			break;
385 		case CD_MAXIPSIZE:
386 		case CD_MTU:
387 		case CD_MAX_DHCP_SIZE:
388 			/* Number: bytes */
389 			display_number("%s = %d bytes", option_types[save],
390 			    &start);
391 			break;
392 		case CD_CLASS_ID:
393 		case CD_CLIENT_ID:
394 		case CD_NW_IP_DOMAIN:
395 		case CD_NW_IP_OPTIONS:
396 			/* Hex ascii strings */
397 			display_ascii_hex(option_types[save], &start);
398 			break;
399 		case CD_BOOT_SIZE:
400 			display_number("%s = %d 512 byte blocks",
401 			    "Boot file size", &start);
402 			break;
403 		case CD_POLICY_FILTER:
404 			if ((*start % 8) != 0) {
405 				(void) sprintf(get_line(0, 0),
406 				    "Error: Bad Policy Filter option");
407 			} else {
408 				items = *start++ / 8;
409 				for (i = 0; i < items; i++) {
410 					display_ip(1,
411 					    "%s = %s",
412 					    "Policy Destination",
413 					    &start);
414 					display_ip(1, "%s = %s", "Mask",
415 					    &start);
416 				}
417 			}
418 			break;
419 		case CD_PATH_MTU_TABLE_SZ:
420 			if (*start % 2 != 0) {
421 				(void) sprintf(get_line(0, 0),
422 				    "Error: Bad Path MTU Table");
423 			} else {
424 				(void) sprintf(get_line(0, 0),
425 				    "\tPath MTU Plateau Table:");
426 				(void) sprintf(get_line(0, 0),
427 				    "\t=======================");
428 				items = *start / sizeof (ushort_t);
429 				++start;
430 				for (i = 0; i < items; i++) {
431 					if (IS_P2ALIGNED(start,
432 					    sizeof (ushort_t))) {
433 						/* LINTED: improper alignment */
434 						s_buf = *(ushort_t *)start;
435 					} else {
436 						memcpy((char *)&s_buf,
437 						    start, sizeof (short));
438 					}
439 					(void) sprintf(get_line(0, 0),
440 					    "\t\tEntry %d:\t\t%d", i,
441 					    ntohs(s_buf));
442 					start += sizeof (ushort_t);
443 				}
444 			}
445 			break;
446 		case CD_STATIC_ROUTE:
447 			if ((*start % 8) != 0) {
448 				(void) sprintf(get_line(0, 0),
449 				    "Error: Bad Static Route option: %d",
450 				    *start);
451 			} else {
452 				items = *start++ / 8;
453 				for (i = 0; i < items; i++) {
454 					memcpy((char *)&tmp, start,
455 					    sizeof (struct in_addr));
456 					(void) strcpy(scratch, inet_ntoa(tmp));
457 					start += sizeof (ulong_t);
458 					memcpy((char *)&tmp, start,
459 					    sizeof (struct in_addr));
460 					(void) sprintf(get_line(0, 0),
461 					    "Static route from %s to %s",
462 					    scratch, inet_ntoa(tmp));
463 					start += sizeof (ulong_t);
464 				}
465 			}
466 			break;
467 		case CD_VENDOR_SPEC:
468 			i = *start++;
469 			(void) sprintf(get_line(0, 0),
470 			    "Vendor-specific Options (%d total octets):", i);
471 			/*
472 			 * We don't know what these things are, so just
473 			 * display the option number, length, and value
474 			 * (hex).
475 			 */
476 			vend = (uchar_t *)((uchar_t *)start + i);
477 			while (start < vend && *start != CD_END) {
478 				if (*start == CD_PAD) {
479 					start++;
480 					continue;
481 				}
482 				(void) sprintf(scratch,
483 				    "\t(%.2d) %.2d octets", *start,
484 				    *(uchar_t *)((uchar_t *)start + 1));
485 				start++;
486 				display_ascii_hex(scratch, &start);
487 			}
488 			start = vend;	/* in case CD_END found */
489 			break;
490 		case CD_NETBIOS_NODE_TYPE:
491 			if (*start != 1) {
492 				(void) sprintf(get_line(0, 0),
493 				    "Error: Bad '%s' parameter",
494 				    option_types[CD_NETBIOS_NODE_TYPE]);
495 			} else {
496 				char *type;
497 				start++;
498 				switch (*start) {
499 				case 0x1:
500 					type = "Broadcast Node";
501 					break;
502 				case 0x2:
503 					type = "Point To Point Node";
504 					break;
505 				case 0x4:
506 					type = "Mixed Mode Node";
507 					break;
508 				case 0x8:
509 					type = "Hybrid Node";
510 					break;
511 				default:
512 					type = "??? Node";
513 					break;
514 				};
515 				(void) sprintf(get_line(0, 0),
516 				    "%s = %s (%d)",
517 				    option_types[CD_NETBIOS_NODE_TYPE],
518 				    type, *start);
519 				start++;
520 			}
521 			break;
522 		case CD_OPTION_OVERLOAD:
523 			if (*start != 1) {
524 				(void) sprintf(get_line(0, 0),
525 				    "Bad Option Overload value.");
526 			} else {
527 				start++;
528 				nooverload = *start++;
529 			}
530 			break;
531 		case CD_DHCP_TYPE:
532 			if (*start < 1 || *start > 7) {
533 				(void) sprintf(get_line(0, 0),
534 				    "Bad DHCP Message Type.");
535 			} else {
536 				start++;
537 				(void) sprintf(get_line(0, 0),
538 				    "Message type = %s",
539 				    show_msgtype(*start));
540 				start++;
541 			}
542 			break;
543 		case CD_REQUEST_LIST:
544 			opt_len = *start++;
545 			(void) sprintf(get_line(0, 0),
546 			    "Requested Options:");
547 			for (i = 0; i < opt_len; i++) {
548 				entry = NULL;
549 				if (*start < OPTIONS_ARRAY_SIZE) {
550 					prmpt = option_types[*start];
551 				} else {
552 					entry = inittab_getbycode(
553 					    ITAB_CAT_STANDARD|ITAB_CAT_SITE,
554 					    ITAB_CONS_SNOOP, *start);
555 					if (entry == NULL) {
556 						if (*start >= DHCP_SITE_OPT &&
557 						    *start <= DHCP_END_SITE) {
558 							prmpt = "Site Option";
559 						} else {
560 							prmpt = "Unrecognized "
561 							    "Option";
562 						}
563 					} else {
564 						prmpt = entry->ds_name;
565 					}
566 				}
567 				(void) sprintf(get_line(0, 0),
568 				    "\t%2d (%s)", *start, prmpt);
569 				start++;
570 				free(entry);
571 			}
572 			break;
573 		default:
574 			opt_len = *start++;
575 			entry = inittab_getbycode(
576 			    ITAB_CAT_STANDARD|ITAB_CAT_SITE,
577 			    ITAB_CONS_SNOOP, save);
578 			if (entry == NULL) {
579 				if (save >= DHCP_SITE_OPT &&
580 				    save <= DHCP_END_SITE)
581 					prmpt = "Site";
582 				else
583 					prmpt = "Unrecognized";
584 				decoded_opt = NULL;
585 			} else {
586 				if (save < OPTIONS_ARRAY_SIZE) {
587 					prmpt = option_types[save];
588 				} else {
589 					prmpt = entry->ds_name;
590 				}
591 				decoded_opt = inittab_decode(entry, start,
592 				    opt_len, B_TRUE);
593 			}
594 			if (decoded_opt == NULL) {
595 				(void) sprintf(get_line(0, 0),
596 				    "%s Option = %d, length = %d octets",
597 				    prmpt, save, opt_len);
598 				start--;
599 				display_ascii_hex("\tValue =", &start);
600 			} else {
601 				(void) sprintf(get_line(0, 0), "%s = %s", prmpt,
602 				    decoded_opt);
603 				start += opt_len;
604 				free(decoded_opt);
605 			}
606 			free(entry);
607 			break;
608 		};
609 	}
610 	return (nooverload);
611 }
612 
613 static const char *
614 show_msgtype(unsigned char type)
615 {
616 	/*
617 	 * note: the ordering here allows direct indexing of the table
618 	 *	 based on the RFC2131 packet type value passed in.
619 	 */
620 
621 	static const char *types[] = {
622 		"BOOTP",
623 		"DHCPDISCOVER", "DHCPOFFER",   "DHCPREQUEST", "DHCPDECLINE",
624 		"DHCPACK",    "DHCPNAK",      "DHCPRELEASE", "DHCPINFORM"
625 	};
626 
627 	if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL)
628 		return ("UNKNOWN");
629 
630 	return (types[type]);
631 }
632 
633 static void
634 display_ip(int items, char *fmt, char *msg, unsigned char **opt)
635 {
636 	struct in_addr tmp;
637 	int i;
638 
639 	for (i = 0; i < items; i++) {
640 		memcpy((char *)&tmp, *opt, sizeof (struct in_addr));
641 		(void) sprintf(get_line(0, 0), fmt, msg, inet_ntoa(tmp));
642 		*opt += 4;
643 	}
644 }
645 
646 static void
647 display_ascii(char *fmt, char *msg, unsigned char **opt)
648 {
649 	static unsigned char buf[256];
650 	int len = **opt;
651 	unsigned char slen = len;
652 
653 	if (len >= sizeof (buf))
654 		len = sizeof (buf) - 1;
655 	(*opt)++;
656 	memcpy(buf, *opt, len);
657 	*(unsigned char *)(buf + len) = '\0';
658 	(void) sprintf(get_line(0, 0), fmt, msg, buf);
659 	(*opt) += slen;
660 }
661 
662 static void
663 display_number(char *fmt, char *msg, unsigned char **opt)
664 {
665 	int len = **opt;
666 	unsigned long l_buf = 0;
667 	unsigned short s_buf = 0;
668 
669 	if (len > 4) {
670 		(*opt)++;
671 		(void) sprintf(get_line(0, 0), fmt, msg, 0xdeadbeef);
672 		return;
673 	}
674 	switch (len) {
675 	case sizeof (uchar_t):
676 		(*opt)++;
677 		(void) sprintf(get_line(0, 0), fmt, msg, **opt);
678 		break;
679 	case sizeof (ushort_t):
680 		(*opt)++;
681 		if (IS_P2ALIGNED(*opt, sizeof (ushort_t)))
682 			/* LINTED: improper alignment */
683 			s_buf = *(unsigned short *)*opt;
684 		else
685 			memcpy((char *)&s_buf, *opt, len);
686 		(void) sprintf(get_line(0, 0), fmt, msg, ntohs(s_buf));
687 		break;
688 	case sizeof (ulong_t):
689 		(*opt)++;
690 		if (IS_P2ALIGNED(*opt, sizeof (ulong_t)))
691 			/* LINTED: improper alignment */
692 			l_buf = *(unsigned long *)*opt;
693 		else
694 			memcpy((char *)&l_buf, *opt, len);
695 		(void) sprintf(get_line(0, 0), fmt, msg, ntohl(l_buf));
696 		break;
697 	}
698 	(*opt) += len;
699 }
700 
701 static void
702 display_ascii_hex(char *msg, unsigned char **opt)
703 {
704 	int printable;
705 	char	buffer[512];
706 	char  *line, *tmp, *ap, *fmt;
707 	int	i, len = **opt;
708 
709 	line = get_line(0, 0);
710 
711 	(*opt)++;
712 
713 	if (len >= 255) {
714 		(void) sprintf(line, "\t%s <TOO LONG>", msg);
715 		return;
716 	}
717 
718 	for (printable = 1, tmp = (char *)(*opt), ap = buffer;
719 	    tmp < (char *)&((*opt)[len]); tmp++) {
720 		if (isprint(*tmp))
721 			*ap++ = *tmp;
722 		else {
723 			*ap++ = '.';
724 			printable = 0;
725 		}
726 	}
727 	*ap = '\0';
728 
729 	if (!printable) {
730 		for (tmp = (char *)(*opt), ap = buffer;
731 		    (tmp < (char *)&((*opt)[len])) && ((ap + 5) < &buffer[512]);
732 		    tmp++) {
733 			ap += sprintf(ap, "0x%02X ", *(uchar_t *)(tmp));
734 		}
735 		/* Truncate the trailing space */
736 		*(--ap) = '\0';
737 		/* More bytes to print in hex but no space in buffer */
738 		if (tmp < (char *)&((*opt)[len])) {
739 			i = ap - buffer;
740 			buffer[i - 1] = '.';
741 			buffer[i - 2] = '.';
742 			buffer[i - 3] = '.';
743 		}
744 		fmt = "%s\t%s (unprintable)";
745 	} else {
746 		fmt = "%s\t\"%s\"";
747 	}
748 	(*opt) += len;
749 	(void) sprintf(line, fmt, msg, buffer);
750 }
751