/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #define DHCP_INFO_VENDOR_START_V4 256 #define DHCP_INFO_VENDOR_START_V6 65536 static void usage(const char *program) { (void) fprintf(stderr, "usage: %s [-c] [-i interface] [-n limit] [-v {4|6}] code\n" " %s [-c] [-i interface] [-n limit] [-v {4|6}] identifier\n", program, program); exit(DHCP_EXIT_BADARGS); } int main(int argc, char **argv) { ssize_t max_lines = -1; size_t gran, n_spaces = 0; dhcp_optnum_t optnum; dhcp_ipc_request_t *request; dhcp_ipc_reply_t *reply; int c, error, i; char *ifname = ""; char *value, *valuep; dhcp_symbol_t *entry; DHCP_OPT *opt; size_t opt_len; boolean_t is_canonical = B_FALSE; long version = 4; boolean_t isv6; uint8_t *valptr; while ((c = getopt(argc, argv, "ci:n:v:")) != EOF) { switch (c) { case 'c': is_canonical = B_TRUE; break; case 'i': ifname = optarg; break; case 'n': max_lines = strtoul(optarg, NULL, 0); break; case 'v': version = strtol(optarg, NULL, 0); if (version != 4 && version != 6) usage(argv[0]); break; case '?': usage(argv[0]); default: break; } } if (argc - optind != 1) usage(argv[0]); /* * we either have a code or an identifer. if we have a code, * then values over 256 indicate a vendor option. if we have * an identifier, then use inittab_getbyname() to turn the * identifier into a code, then send the request over the wire. */ isv6 = (version == 6); if (isalpha(*argv[optind])) { entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD | ITAB_CAT_VENDOR | ITAB_CAT_FIELD | (isv6 ? ITAB_CAT_V6 : 0), ITAB_CONS_INFO, argv[optind]); if (entry == NULL) { (void) fprintf(stderr, "%s: unknown identifier `%s'\n", argv[0], argv[optind]); return (DHCP_EXIT_BADARGS); } optnum.code = entry->ds_code; optnum.category = entry->ds_category; } else { ulong_t start; optnum.code = strtoul(argv[optind], 0, 0); optnum.category = ITAB_CAT_STANDARD | ITAB_CAT_SITE; /* * sigh. this is a hack, but it's needed for backward * compatibility with the CA dhcpinfo program. */ start = isv6 ? DHCP_INFO_VENDOR_START_V6 : DHCP_INFO_VENDOR_START_V4; if (optnum.code > start) { optnum.code -= start; optnum.category = ITAB_CAT_VENDOR; } if (isv6) optnum.category |= ITAB_CAT_V6; entry = inittab_getbycode(optnum.category, ITAB_CONS_INFO, optnum.code); if (entry == NULL) { (void) fprintf(stderr, "%s: unknown code `%s'\n", argv[0], argv[optind]); return (DHCP_EXIT_BADARGS); } optnum.category = entry->ds_category; } optnum.size = entry->ds_max * inittab_type_to_size(entry); /* * send the request to the agent and reap the reply */ request = dhcp_ipc_alloc_request(DHCP_GET_TAG | (isv6 ? DHCP_V6 : 0), ifname, &optnum, sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM); if (request == NULL) return (DHCP_EXIT_SYSTEM); error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT); if (error != 0 || reply->return_code != 0) { if (error == 0) error = reply->return_code; (void) fprintf(stderr, "%s: %s\n", argv[0], dhcp_ipc_strerror(error)); if (error == DHCP_IPC_E_TIMEOUT) return (DHCP_EXIT_TIMEOUT); return (DHCP_EXIT_FAILURE); } opt = dhcp_ipc_get_data(reply, &opt_len, NULL); /* * no data means that the client has an ACK but has no information * about the specified option; return success */ if (opt_len == 0) return (DHCP_EXIT_SUCCESS); /* * check for protocol error */ if (isv6) { dhcpv6_option_t d6o; if (opt_len < sizeof (d6o)) return (DHCP_EXIT_FAILURE); (void) memcpy(&d6o, opt, sizeof (d6o)); if (opt_len != ntohs(d6o.d6o_len) + sizeof (d6o)) return (DHCP_EXIT_FAILURE); valptr = (uint8_t *)opt + sizeof (d6o); opt_len -= sizeof (d6o); } else { if (opt_len < 2 || (opt_len - 2 != opt->len)) return (DHCP_EXIT_FAILURE); opt_len -= 2; valptr = opt->value; } if (is_canonical) { value = malloc(opt_len * (sizeof ("0xNN") + 1)); if (value == NULL) { (void) fprintf(stderr, "%s: out of memory\n", argv[0]); return (DHCP_EXIT_FAILURE); } for (i = 0, valuep = value; i < opt_len; i++) valuep += sprintf(valuep, "0x%02X ", valptr[i]); valuep[-1] = '\0'; gran = 1; } else { value = inittab_decode(entry, valptr, opt_len, B_TRUE); if (value == NULL) { (void) fprintf(stderr, "%s: cannot decode agent's " "reply\n", argv[0]); return (DHCP_EXIT_FAILURE); } gran = entry->ds_gran; } /* * now display `gran' items per line, printing at most `max_lines'. */ for (i = 0; value[i] != '\0'; i++) { if (value[i] == ' ') { if ((++n_spaces % gran) == 0) { value[i] = '\n'; if (max_lines != -1 && --max_lines == 0) { value[i] = '\0'; break; } } } } (void) printf("%s\n", value); return (DHCP_EXIT_SUCCESS); }