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
usage(const char * program)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
main(int argc,char ** argv)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