xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpinfo/dhcpinfo.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 
40 static void
41 usage(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 
51 int
52 main(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