2 * dhcpclient.c General radius packet debug tool.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2010 Alan DeKok <aland@ox.org>
27 #include <freeradius-devel/libradius.h>
28 #include <freeradius-devel/conf.h>
29 #include <freeradius-devel/radpaths.h>
30 #include <freeradius-devel/dhcp.h>
42 static int success = 0;
43 static int retries = 3;
44 static float timeout = 5;
46 static uint16_t server_port = 0;
47 static int packet_code = 0;
48 static fr_ipaddr_t server_ipaddr;
50 static fr_ipaddr_t client_ipaddr;
51 static uint16_t client_port = 0;
55 static RADIUS_PACKET *request = NULL;
56 static RADIUS_PACKET *reply = NULL;
58 #define DHCP_CHADDR_LEN (16)
59 #define DHCP_SNAME_LEN (64)
60 #define DHCP_FILE_LEN (128)
61 #define DHCP_VEND_LEN (308)
62 #define DHCP_OPTION_MAGIC_NUMBER (0x63825363)
64 char const *dhcpclient_version = "dhcpclient version " RADIUSD_VERSION_STRING
65 #ifdef RADIUSD_VERSION_COMMIT
66 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
68 ", built on " __DATE__ " at " __TIME__;
70 static void NEVER_RETURNS usage(void)
72 fprintf(stderr, "Usage: dhcpclient [options] server[:port] <command>\n");
73 fprintf(stderr, "Send a DHCP request with provided RADIUS attrs and output response.\n");
75 fprintf(stderr, " <command> One of discover, request, offer, decline, release, inform.\n");
76 fprintf(stderr, " -d <directory> Set the directory where the dictionaries are stored (defaults to " RADDBDIR ").\n");
77 fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
78 fprintf(stderr, " -f <file> Read packets from file, not stdin.\n");
79 fprintf(stderr, " -t <timeout> Wait 'timeout' seconds for a reply (may be a floating point number).\n");
80 fprintf(stderr, " -v Show program version information.\n");
81 fprintf(stderr, " -x Debugging mode.\n");
88 * Initialize the request.
90 static int request_init(char const *filename)
95 bool filedone = false;
98 * Determine where to read the VP's from.
101 fp = fopen(filename, "r");
103 fprintf(stderr, "dhcpclient: Error opening %s: %s\n",
104 filename, fr_syserror(errno));
111 request = rad_alloc(NULL, false);
116 if (readvp2(NULL, &request->vps, fp, &filedone) < 0) {
117 fr_perror("dhcpclient");
119 if (fp != stdin) fclose(fp);
124 * Fix / set various options
126 for (vp = fr_cursor_init(&cursor, &request->vps); vp; vp = fr_cursor_next(&cursor)) {
127 switch (vp->da->attr) {
132 * Allow it to set the packet type in
133 * the attributes read from the file.
136 request->code = vp->vp_integer;
139 case PW_PACKET_DST_PORT:
140 request->dst_port = (vp->vp_integer & 0xffff);
143 case PW_PACKET_DST_IP_ADDRESS:
144 request->dst_ipaddr.af = AF_INET;
145 request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
148 case PW_PACKET_DST_IPV6_ADDRESS:
149 request->dst_ipaddr.af = AF_INET6;
150 request->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
153 case PW_PACKET_SRC_PORT:
154 request->src_port = (vp->vp_integer & 0xffff);
157 case PW_PACKET_SRC_IP_ADDRESS:
158 request->src_ipaddr.af = AF_INET;
159 request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
162 case PW_PACKET_SRC_IPV6_ADDRESS:
163 request->src_ipaddr.af = AF_INET6;
164 request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
166 } /* switch over the attribute */
168 } /* loop over the VP's we read in */
170 if (fp != stdin) fclose(fp);
178 static char const *dhcp_header_names[] = {
180 "DHCP-Hardware-Type",
181 "DHCP-Hardware-Address-Length",
183 "DHCP-Transaction-Id",
184 "DHCP-Number-of-Seconds",
186 "DHCP-Client-IP-Address",
187 "DHCP-Your-IP-Address",
188 "DHCP-Server-IP-Address",
189 "DHCP-Gateway-IP-Address",
190 "DHCP-Client-Hardware-Address",
191 "DHCP-Server-Host-Name",
192 "DHCP-Boot-Filename",
197 static int dhcp_header_sizes[] = {
207 static void print_hex(RADIUS_PACKET *packet)
210 uint8_t const *p, *a;
212 if (!packet->data) return;
214 if (packet->data_len < 244) {
219 printf("----------------------------------------------------------------------\n");
223 for (i = 0; i < 14; i++) {
224 printf("%s = 0x", dhcp_header_names[i]);
225 for (j = 0; j < dhcp_header_sizes[i]; j++) {
226 printf("%02x", p[j]);
230 p += dhcp_header_sizes[i];
236 printf("%02x %02x %02x %02x\n",
237 p[0], p[1], p[2], p[3]);
240 while (p < (packet->data + packet->data_len)) {
243 if (*p == 255) break; /* end of options signifier */
244 if ((p + 2) > (packet->data + packet->data_len)) break;
246 printf("%02x %02x ", p[0], p[1]);
249 for (i = 0; i < p[1]; i++) {
250 if ((i > 0) && ((i & 0x0f) == 0x00))
252 printf("%02x ", a[i]);
253 if ((i & 0x0f) == 0x0f) printf("\n");
256 if ((p[1] & 0x0f) != 0x00) printf("\n");
260 printf("\n----------------------------------------------------------------------\n");
264 int main(int argc, char **argv)
268 char const *radius_dir = RADDBDIR;
269 char const *dict_dir = DICTDIR;
270 char const *filename = NULL;
275 while ((c = getopt(argc, argv, "d:D:f:hr:t:vx")) != EOF) switch (c) {
287 if (!isdigit((int) *optarg))
289 retries = atoi(optarg);
290 if ((retries == 0) || (retries > 1000)) usage();
293 if (!isdigit((int) *optarg))
295 timeout = atof(optarg);
298 printf("%s\n", dhcpclient_version);
310 argc -= (optind - 1);
311 argv += (optind - 1);
313 if (argc < 2) usage();
315 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
316 fr_perror("dhcpclient");
321 * Ensure that dictionary.dhcp is loaded.
323 da = dict_attrbyname("DHCP-Message-Type");
325 if (dict_read(dict_dir, "dictionary.dhcp") < 0) {
326 fprintf(stderr, "Failed reading dictionary.dhcp: %s",
331 if (dict_read(dict_dir, "dictionary.freeradius.internal") < 0) {
332 fprintf(stderr, "Failed reading dictionary.freeradius.internal: %s",
341 server_ipaddr.af = AF_INET;
342 if (strcmp(argv[1], "-") != 0) {
343 char const *hostname = argv[1];
344 char const *portname = argv[1];
347 if (*argv[1] == '[') { /* IPv6 URL encoded */
348 p = strchr(argv[1], ']');
349 if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
353 memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
354 buffer[p - argv[1] - 1] = '\0';
360 p = strchr(portname, ':');
361 if (p && (strchr(p + 1, ':') == NULL)) {
368 if (ip_hton(&server_ipaddr, AF_INET, hostname, false) < 0) {
369 fprintf(stderr, "dhcpclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno));
374 * Strip port from hostname if needed.
376 if (portname) server_port = atoi(portname);
380 * See what kind of request we want to send.
382 if (strcmp(argv[2], "discover") == 0) {
383 if (server_port == 0) server_port = 67;
384 packet_code = PW_DHCP_DISCOVER;
386 } else if (strcmp(argv[2], "request") == 0) {
387 if (server_port == 0) server_port = 67;
388 packet_code = PW_DHCP_REQUEST;
390 } else if (strcmp(argv[2], "offer") == 0) {
391 if (server_port == 0) server_port = 67;
392 packet_code = PW_DHCP_OFFER;
394 } else if (strcmp(argv[2], "decline") == 0) {
395 if (server_port == 0) server_port = 67;
396 packet_code = PW_DHCP_DECLINE;
398 } else if (strcmp(argv[2], "release") == 0) {
399 if (server_port == 0) server_port = 67;
400 packet_code = PW_DHCP_RELEASE;
402 } else if (strcmp(argv[2], "inform") == 0) {
403 if (server_port == 0) server_port = 67;
404 packet_code = PW_DHCP_INFORM;
406 } else if (isdigit((int) argv[2][0])) {
407 if (server_port == 0) server_port = 67;
408 packet_code = atoi(argv[2]);
410 fprintf(stderr, "Unknown packet type %s\n", argv[2]);
414 request_init(filename);
419 if (!request || !request->vps) {
420 fprintf(stderr, "dhcpclient: Nothing to send.\n");
423 request->code = packet_code;
426 * Bind to the first specified IP address and port.
427 * This means we ignore later ones.
429 if (request->src_ipaddr.af == AF_UNSPEC) {
430 memset(&client_ipaddr, 0, sizeof(client_ipaddr));
431 client_ipaddr.af = server_ipaddr.af;
434 client_ipaddr = request->src_ipaddr;
435 client_port = request->src_port;
437 sockfd = fr_socket(&client_ipaddr, client_port);
439 fprintf(stderr, "dhcpclient: socket: %s\n", fr_strerror());
444 * Set option 'receive timeout' on socket.
445 * Note: in case of a timeout, the error will be "Resource temporarily unavailable".
448 tv.tv_sec = (time_t)timeout;
449 tv.tv_usec = (uint64_t)(timeout * 1000000) - (tv.tv_sec * 1000000);
450 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)) == -1) {
451 fprintf(stderr, "dhcpclient: failed setting socket timeout: %s\n",
456 request->sockfd = sockfd;
457 if (request->src_ipaddr.af == AF_UNSPEC) {
458 request->src_ipaddr = client_ipaddr;
459 request->src_port = client_port;
461 if (request->dst_ipaddr.af == AF_UNSPEC) {
462 request->dst_ipaddr = server_ipaddr;
463 request->dst_port = server_port;
469 if (fr_dhcp_encode(request) < 0) {
470 fprintf(stderr, "dhcpclient: failed encoding: %s\n",
474 if (fr_debug_flag) print_hex(request);
476 if (fr_dhcp_send(request) < 0) {
477 fprintf(stderr, "dhcpclient: failed sending: %s\n",
482 reply = fr_dhcp_recv(sockfd);
484 fprintf(stderr, "dhcpclient: Error receiving reply %s\n", fr_strerror());
487 if (fr_debug_flag) print_hex(reply);
489 if (fr_dhcp_decode(reply) < 0) {
490 fprintf(stderr, "dhcpclient: failed decoding\n");
496 if (success) return 0;
501 #endif /* WITH_DHCP */