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 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/types.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <ctype.h>
32#include <string.h>
33#include <dhcpagent_ipc.h>
34#include <dhcp_inittab.h>
35#include <dhcp_symbol.h>
36
37#define	DHCP_INFO_VENDOR_START_V4	256
38#define	DHCP_INFO_VENDOR_START_V6	65536
39
40static void
41usage(const char *program)
42{
43	(void) fprintf(stderr,
44	    "usage: %s [-c] [-i interface] [-n limit] [-v {4|6}] code\n"
45	    "       %s [-c] [-i interface] [-n limit] [-v {4|6}] identifier\n",
46	    program, program);
47
48	exit(DHCP_EXIT_BADARGS);
49}
50
51int
52main(int argc, char **argv)
53{
54	ssize_t			max_lines = -1;
55	size_t			gran, n_spaces = 0;
56	dhcp_optnum_t		optnum;
57	dhcp_ipc_request_t	*request;
58	dhcp_ipc_reply_t	*reply;
59	int			c, error, i;
60	char			*ifname = "";
61	char			*value, *valuep;
62	dhcp_symbol_t		*entry;
63	DHCP_OPT		*opt;
64	size_t			opt_len;
65	boolean_t		is_canonical = B_FALSE;
66	long			version = 4;
67	boolean_t		isv6;
68	uint8_t			*valptr;
69
70	while ((c = getopt(argc, argv, "ci:n:v:")) != EOF) {
71
72		switch (c) {
73
74		case 'c':
75			is_canonical = B_TRUE;
76			break;
77
78		case 'i':
79			ifname = optarg;
80			break;
81
82		case 'n':
83			max_lines = strtoul(optarg, NULL, 0);
84			break;
85
86		case 'v':
87			version = strtol(optarg, NULL, 0);
88			if (version != 4 && version != 6)
89				usage(argv[0]);
90			break;
91
92		case '?':
93			usage(argv[0]);
94
95		default:
96			break;
97		}
98	}
99
100	if (argc - optind != 1)
101		usage(argv[0]);
102
103	/*
104	 * we either have a code or an identifer.  if we have a code,
105	 * then values over 256 indicate a vendor option.  if we have
106	 * an identifier, then use inittab_getbyname() to turn the
107	 * identifier into a code, then send the request over the wire.
108	 */
109
110	isv6 = (version == 6);
111
112	if (isalpha(*argv[optind])) {
113
114		entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD |
115		    ITAB_CAT_VENDOR | ITAB_CAT_FIELD |
116		    (isv6 ? ITAB_CAT_V6 : 0), ITAB_CONS_INFO,
117		    argv[optind]);
118
119		if (entry == NULL) {
120			(void) fprintf(stderr, "%s: unknown identifier `%s'\n",
121			    argv[0], argv[optind]);
122			return (DHCP_EXIT_BADARGS);
123		}
124
125		optnum.code	= entry->ds_code;
126		optnum.category = entry->ds_category;
127
128	} else {
129		ulong_t start;
130
131		optnum.code	= strtoul(argv[optind], 0, 0);
132		optnum.category = ITAB_CAT_STANDARD | ITAB_CAT_SITE;
133
134		/*
135		 * sigh.  this is a hack, but it's needed for backward
136		 * compatibility with the CA dhcpinfo program.
137		 */
138
139		start = isv6 ? DHCP_INFO_VENDOR_START_V6 :
140		    DHCP_INFO_VENDOR_START_V4;
141		if (optnum.code > start) {
142			optnum.code    -= start;
143			optnum.category = ITAB_CAT_VENDOR;
144		}
145
146		if (isv6)
147			optnum.category |= ITAB_CAT_V6;
148
149		entry = inittab_getbycode(optnum.category, ITAB_CONS_INFO,
150		    optnum.code);
151
152		if (entry == NULL) {
153			(void) fprintf(stderr, "%s: unknown code `%s'\n",
154			    argv[0], argv[optind]);
155			return (DHCP_EXIT_BADARGS);
156		}
157		optnum.category = entry->ds_category;
158	}
159
160	optnum.size = entry->ds_max * inittab_type_to_size(entry);
161
162	/*
163	 * send the request to the agent and reap the reply
164	 */
165
166	request = dhcp_ipc_alloc_request(DHCP_GET_TAG | (isv6 ? DHCP_V6 : 0),
167	    ifname, &optnum, sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
168
169	if (request == NULL)
170		return (DHCP_EXIT_SYSTEM);
171
172	error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
173	if (error != 0 || reply->return_code != 0) {
174
175		if (error == 0)
176			error = reply->return_code;
177
178		(void) fprintf(stderr, "%s: %s\n", argv[0],
179		    dhcp_ipc_strerror(error));
180
181		if (error == DHCP_IPC_E_TIMEOUT)
182			return (DHCP_EXIT_TIMEOUT);
183
184		return (DHCP_EXIT_FAILURE);
185	}
186
187	opt = dhcp_ipc_get_data(reply, &opt_len, NULL);
188
189	/*
190	 * no data means that the client has an ACK but has no information
191	 * about the specified option; return success
192	 */
193
194	if (opt_len == 0)
195		return (DHCP_EXIT_SUCCESS);
196
197	/*
198	 * check for protocol error
199	 */
200
201	if (isv6) {
202		dhcpv6_option_t d6o;
203
204		if (opt_len < sizeof (d6o))
205			return (DHCP_EXIT_FAILURE);
206		(void) memcpy(&d6o, opt, sizeof (d6o));
207		if (opt_len != ntohs(d6o.d6o_len) + sizeof (d6o))
208			return (DHCP_EXIT_FAILURE);
209		valptr = (uint8_t *)opt + sizeof (d6o);
210		opt_len -= sizeof (d6o);
211	} else {
212		if (opt_len < 2 || (opt_len - 2 != opt->len))
213			return (DHCP_EXIT_FAILURE);
214		opt_len -= 2;
215		valptr = opt->value;
216	}
217
218	if (is_canonical) {
219
220		value = malloc(opt_len * (sizeof ("0xNN") + 1));
221		if (value == NULL) {
222			(void) fprintf(stderr, "%s: out of memory\n", argv[0]);
223			return (DHCP_EXIT_FAILURE);
224		}
225
226		for (i = 0, valuep = value; i < opt_len; i++)
227			valuep += sprintf(valuep, "0x%02X ", valptr[i]);
228
229		valuep[-1] = '\0';
230		gran = 1;
231
232	} else {
233
234		value = inittab_decode(entry, valptr, opt_len, B_TRUE);
235		if (value == NULL) {
236			(void) fprintf(stderr, "%s: cannot decode agent's "
237			    "reply\n", argv[0]);
238			return (DHCP_EXIT_FAILURE);
239		}
240
241		gran = entry->ds_gran;
242	}
243
244	/*
245	 * now display `gran' items per line, printing at most `max_lines'.
246	 */
247
248	for (i = 0; value[i] != '\0'; i++) {
249		if (value[i] == ' ') {
250			if ((++n_spaces % gran) == 0) {
251				value[i] = '\n';
252				if (max_lines != -1 && --max_lines == 0) {
253					value[i] = '\0';
254					break;
255				}
256			}
257		}
258	}
259
260	(void) printf("%s\n", value);
261
262	return (DHCP_EXIT_SUCCESS);
263}
264