All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dhcpclient.c
Go to the documentation of this file.
1 /*
2  * dhcpclient.c General radius packet debug tool.
3  *
4  * Version: $Id: c570f5ca265824db7128e62ae521776deb59d889 $
5  *
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.
10  *
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.
15  *
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
19  *
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>
23  */
24 
25 RCSID("$Id: c570f5ca265824db7128e62ae521776deb59d889 $")
26 
27 #include <freeradius-devel/libradius.h>
28 #include <freeradius-devel/conf.h>
29 #include <freeradius-devel/dhcp.h>
30 #include <freeradius-devel/pcap.h>
31 
32 /*
33  * Logging macros
34  */
35  #undef DEBUG
36 #define DEBUG(fmt, ...) if (fr_debug_lvl > 0) fr_printf_log(fmt "\n", ## __VA_ARGS__)
37 #undef DEBUG2
38 #define DEBUG2(fmt, ...) if (fr_debug_lvl > 1) fr_printf_log(fmt "\n", ## __VA_ARGS__)
39 
40 
41 #define ERROR(fmt, ...) fr_perror("dhcpclient: " fmt, ## __VA_ARGS__)
42 
43 #ifdef WITH_DHCP
44 
45 #include <ctype.h>
46 
47 #ifdef HAVE_GETOPT_H
48 # include <getopt.h>
49 #endif
50 
51 #include <assert.h>
52 
53 #include <net/if.h>
54 
55 static int retries = 3;
56 static float timeout = 5.0;
57 static struct timeval tv_timeout;
58 
59 static int sockfd;
60 #ifdef HAVE_LIBPCAP
61 static fr_pcap_t *pcap;
62 #define ETH_ADDR_LEN 6
63 static uint8_t eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
64 #endif
65 
66 static char *iface = NULL;
67 static int iface_ind = -1;
68 
69 #ifdef HAVE_LINUX_IF_PACKET_H
70 struct sockaddr_ll ll; /* Socket address structure */
71 #endif
72 
73 
74 static bool raw_mode = false;
75 
76 static bool reply_expected = true;
77 
78 #define DHCP_CHADDR_LEN (16)
79 #define DHCP_SNAME_LEN (64)
80 #define DHCP_FILE_LEN (128)
81 
82 static char const *dhcpclient_version = "dhcpclient version " RADIUSD_VERSION_STRING
83 #ifdef RADIUSD_VERSION_COMMIT
84 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
85 #endif
86 ", built on " __DATE__ " at " __TIME__;
87 
88 /* structure to keep track of offered IP addresses */
89 typedef struct dc_offer {
90  uint32_t server_addr;
91  uint32_t offered_addr;
92 } dc_offer_t;
93 
94 static const FR_NAME_NUMBER request_types[] = {
95  { "discover", PW_DHCP_DISCOVER },
96  { "request", PW_DHCP_REQUEST },
97  { "decline", PW_DHCP_DECLINE },
98  { "release", PW_DHCP_RELEASE },
99  { "inform", PW_DHCP_INFORM },
100  { "lease_query", PW_DHCP_LEASE_QUERY },
101  { "auto", PW_CODE_UNDEFINED },
102  { NULL, 0}
103 };
104 
105 static void NEVER_RETURNS usage(void)
106 {
107  DEBUG("Usage: dhcpclient [options] server[:port] [<command>]");
108  DEBUG("Send a DHCP request with provided RADIUS attrs and output response.");
109 
110  DEBUG(" <command> One of: discover, request, decline, release, inform; or: auto.");
111  DEBUG(" -d <directory> Set the directory where the dictionaries are stored (defaults to " RADDBDIR ").");
112  DEBUG(" -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").");
113  DEBUG(" -f <file> Read packets from file, not stdin.");
114  DEBUG(" -i <interface> Use this interface to send/receive at packet level on a raw socket.");
115  DEBUG(" -t <timeout> Wait 'timeout' seconds for a reply (may be a floating point number).");
116  DEBUG(" -v Show program version information.");
117  DEBUG(" -x Debugging mode.");
118 
119  exit(1);
120 }
121 
122 
123 /*
124  * Initialize the request.
125  */
126 static RADIUS_PACKET *request_init(char const *filename)
127 {
128  FILE *fp;
129  vp_cursor_t cursor;
130  VALUE_PAIR *vp;
131  bool filedone = false;
132  RADIUS_PACKET *request;
133 
134  /*
135  * Determine where to read the VP's from.
136  */
137  if (filename) {
138  fp = fopen(filename, "r");
139  if (!fp) {
140  ERROR("Error opening %s: %s", filename, fr_syserror(errno));
141  return NULL;
142  }
143  } else {
144  fp = stdin;
145  }
146 
147  request = fr_radius_alloc(NULL, false);
148  /*
149  * Read the VP's.
150  */
151  if (fr_pair_list_afrom_file(NULL, &request->vps, fp, &filedone) < 0) {
152  fr_perror("dhcpclient");
153  fr_radius_free(&request);
154  if (fp != stdin) fclose(fp);
155  return NULL;
156  }
157 
158  /*
159  * Fix / set various options
160  */
161  for (vp = fr_cursor_init(&cursor, &request->vps);
162  vp;
163  vp = fr_cursor_next(&cursor)) {
164  /*
165  * Allow to set packet type using DHCP-Message-Type
166  */
167  if (vp->da->vendor == DHCP_MAGIC_VENDOR && vp->da->attr == PW_DHCP_MESSAGE_TYPE) {
168  request->code = vp->vp_integer + PW_DHCP_OFFSET;
169  } else if (!vp->da->vendor) switch (vp->da->attr) {
170  /*
171  * Allow it to set the packet type in
172  * the attributes read from the file.
173  * (this takes precedence over the command argument.)
174  */
175  case PW_PACKET_TYPE:
176  request->code = vp->vp_integer;
177  break;
178 
179  case PW_PACKET_DST_PORT:
180  request->dst_port = (vp->vp_integer & 0xffff);
181  break;
182 
183  case PW_PACKET_DST_IP_ADDRESS:
184  request->dst_ipaddr.af = AF_INET;
185  request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
186  request->dst_ipaddr.prefix = 32;
187  break;
188 
189  case PW_PACKET_DST_IPV6_ADDRESS:
190  request->dst_ipaddr.af = AF_INET6;
191  request->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
192  request->dst_ipaddr.prefix = 128;
193  break;
194 
195  case PW_PACKET_SRC_PORT:
196  request->src_port = (vp->vp_integer & 0xffff);
197  break;
198 
199  case PW_PACKET_SRC_IP_ADDRESS:
200  request->src_ipaddr.af = AF_INET;
201  request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
202  request->src_ipaddr.prefix = 32;
203  break;
204 
205  case PW_PACKET_SRC_IPV6_ADDRESS:
206  request->src_ipaddr.af = AF_INET6;
207  request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
208  request->src_ipaddr.prefix = 128;
209  break;
210 
211  default:
212  break;
213  } /* switch over the attribute */
214 
215  } /* loop over the VP's we read in */
216 
217  if (fp != stdin) fclose(fp);
218 
219  /*
220  * And we're done.
221  */
222  return request;
223 }
224 
225 static int dhcp_header_sizes[] = {
226  1, 1, 1, 1,
227  4, 2, 2, 4,
228  4, 4, 4,
232 };
233 
234 
235 static void print_hex(RADIUS_PACKET *packet)
236 {
237  int i, j;
238  uint8_t const *p, *a;
239 
240  if (!packet->data) return;
241 
242  if (packet->data_len < 244) {
243  printf("Huh?\n");
244  return;
245  }
246 
247  printf("----------------------------------------------------------------------\n");
248  fflush(stdout);
249 
250  p = packet->data;
251  for (i = 0; i < 14; i++) {
252  printf("%s = 0x", dhcp_header_names[i]);
253  for (j = 0; j < dhcp_header_sizes[i]; j++) {
254  printf("%02x", p[j]);
255 
256  }
257  printf("\n");
258  p += dhcp_header_sizes[i];
259  }
260 
261  /*
262  * Magic number
263  */
264  printf("%02x %02x %02x %02x\n",
265  p[0], p[1], p[2], p[3]);
266  p += 4;
267 
268  while (p < (packet->data + packet->data_len)) {
269 
270  if (*p == 0) break;
271  if (*p == 255) break; /* end of options signifier */
272  if ((p + 2) > (packet->data + packet->data_len)) break;
273 
274  printf("%02x %02x ", p[0], p[1]);
275  a = p + 2;
276 
277  for (i = 0; i < p[1]; i++) {
278  if ((i > 0) && ((i & 0x0f) == 0x00))
279  printf("\t\t");
280  printf("%02x ", a[i]);
281  if ((i & 0x0f) == 0x0f) printf("\n");
282  }
283 
284  if ((p[1] & 0x0f) != 0x00) printf("\n");
285 
286  p += p[1] + 2;
287  }
288  printf("\n----------------------------------------------------------------------\n");
289  fflush(stdout);
290 }
291 
292 /*
293  * Loop waiting for DHCP replies until timer expires.
294  * Note that there may be more than one reply: multiple DHCP servers can respond to a broadcast discover.
295  * A real client would pick one of the proposed replies.
296  * We'll just return the first eligible reply, and display the others.
297  */
298 #if defined(HAVE_LINUX_IF_PACKET_H) || defined (HAVE_LIBPCAP)
299 static RADIUS_PACKET *fr_dhcp_recv_raw_loop(int lsockfd,
300 #ifdef HAVE_LINUX_IF_PACKET_H
301  struct sockaddr_ll *p_ll,
302 #endif
303  RADIUS_PACKET *request_p)
304 {
305  struct timeval tval;
306  RADIUS_PACKET *reply_p = NULL;
307  RADIUS_PACKET *cur_reply_p = NULL;
308  int nb_reply = 0;
309  int nb_offer = 0;
310  dc_offer_t *offer_list = NULL;
311  fd_set read_fd;
312  int retval;
313 
314  memcpy(&tval, &tv_timeout, sizeof(struct timeval));
315 
316  /* Loop waiting for DHCP replies until timer expires */
317  while (timerisset(&tval)) {
318  if ((!reply_p) || (cur_reply_p)) { // only debug at start and each time we get a valid DHCP reply on raw socket
319  DEBUG("Waiting for %s DHCP replies for: %d.%06d",
320  (nb_reply>0)?" additional ":" ", (int)tval.tv_sec, (int)tval.tv_usec);
321  }
322 
323  cur_reply_p = NULL;
324  FD_ZERO(&read_fd);
325  FD_SET(lsockfd, &read_fd);
326  retval = select(lsockfd + 1, &read_fd, NULL, NULL, &tval);
327  if (retval < 0) {
328  fr_strerror_printf("Select on DHCP socket failed: %s", fr_syserror(errno));
329  return NULL;
330  }
331 
332  if (retval > 0 && FD_ISSET(lsockfd, &read_fd)) {
333  /* There is something to read on our socket */
334 
335 #ifdef HAVE_LINUX_IF_PACKET_H
336  cur_reply_p = fr_dhcp_recv_raw_packet(lsockfd, p_ll, request_p);
337 #else
338 # ifdef HAVE_LIBPCAP
339  cur_reply_p = fr_dhcp_recv_pcap(pcap);
340 # else
341 # error Need <if/packet.h> or <pcap.h>
342 # endif
343 #endif
344  } else {
345  // Not all implementations of select clear the timer
346  timerclear(&tval);
347  }
348 
349  if (cur_reply_p) {
350  nb_reply ++;
351 
352  if (fr_debug_lvl) print_hex(cur_reply_p);
353 
354  if (fr_dhcp_decode(cur_reply_p) < 0) {
355  ERROR("Failed decoding reply");
356  return NULL;
357  }
358 
359  if (!reply_p) reply_p = cur_reply_p;
360 
361  if (cur_reply_p->code == PW_DHCP_OFFER) {
362  VALUE_PAIR *vp1 = fr_pair_find_by_num(cur_reply_p->vps, DHCP_MAGIC_VENDOR, 54, TAG_ANY); /* DHCP-DHCP-Server-Identifier */
363  VALUE_PAIR *vp2 = fr_pair_find_by_num(cur_reply_p->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-address */
364 
365  if (vp1 && vp2) {
366  nb_offer ++;
367  offer_list = talloc_realloc(request_p, offer_list, dc_offer_t, nb_offer);
368  offer_list[nb_offer-1].server_addr = vp1->vp_ipaddr;
369  offer_list[nb_offer-1].offered_addr = vp2->vp_ipaddr;
370  }
371  }
372  }
373  }
374 
375  if (0 == nb_reply) {
376  DEBUG("No valid DHCP reply received");
377  return NULL;
378  }
379 
380  /* display offer(s) received */
381  if (nb_offer > 0 ) {
382  DEBUG("Received %d DHCP Offer(s):", nb_offer);
383  int i;
384  for (i = 0; i < nb_reply; i++) {
385  char server_addr_buf[INET6_ADDRSTRLEN];
386  char offered_addr_buf[INET6_ADDRSTRLEN];
387 
388  DEBUG("IP address: %s offered by DHCP server: %s",
389  inet_ntop(AF_INET, &offer_list[i].offered_addr,
390  offered_addr_buf, sizeof(offered_addr_buf)),
391  inet_ntop(AF_INET, &offer_list[i].server_addr,
392  server_addr_buf, sizeof(server_addr_buf))
393  );
394  }
395  }
396 
397  return reply_p;
398 }
399 #endif /* <if/packet.h> or <pcap.h> */
400 
401 static int send_with_socket(RADIUS_PACKET **reply, RADIUS_PACKET *request)
402 {
403  int on = 1;
404 
405 #ifdef HAVE_LINUX_IF_PACKET_H
406  if (raw_mode) {
407  sockfd = fr_socket_packet(iface_ind, &ll);
408  } else
409 #endif
410  {
411  sockfd = fr_socket(&request->src_ipaddr, request->src_port);
412  }
413 
414  if (sockfd < 0) {
415  ERROR("Error opening socket");
416  return -1;
417  }
418 
419  /*
420  * Set option 'receive timeout' on socket.
421  * Note: in case of a timeout, the error will be "Resource temporarily unavailable".
422  */
423  if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout, sizeof(struct timeval)) == -1) {
424  ERROR("Failed setting socket timeout: %s", fr_syserror(errno));
425  return -1;
426  }
427 
428  if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
429  ERROR("Can't set broadcast option: %s", fr_syserror(errno));
430  return -1;
431  }
432  request->sockfd = sockfd;
433 
434 #ifdef HAVE_LINUX_IF_PACKET_H
435  if (raw_mode) {
436  if (fr_dhcp_send_raw_packet(sockfd, &ll, request) < 0) {
437  ERROR("Failed sending (fr_dhcp_send_raw_packet): %s", fr_syserror(errno));
438  return -1;
439  }
440  if (!reply_expected) return 0;
441 
442  *reply = fr_dhcp_recv_raw_loop(sockfd, &ll, request);
443  if (!*reply) {
444  ERROR("Error receiving reply (fr_dhcp_recv_raw_loop)");
445  return -1;
446  }
447  } else
448 #endif
449  {
450  if (fr_dhcp_send_socket(request) < 0) {
451  ERROR("Failed sending: %s", fr_syserror(errno));
452  return -1;
453  }
454  if (!reply_expected) return 0;
455 
456  *reply = fr_dhcp_recv_socket(sockfd);
457  if (!*reply) {
458  if (errno == EAGAIN) {
459  fr_strerror(); /* clear error */
460  ERROR("Timed out waiting for reply");
461  } else {
462  ERROR("Error receiving reply");
463  }
464  return -1;
465  }
466  }
467 
468  return 0;
469 }
470 
471 #ifdef HAVE_LIBPCAP
472 static int send_with_pcap(RADIUS_PACKET **reply, RADIUS_PACKET *request)
473 {
474  char ip[16];
475  char pcap_filter[255];
476 
477  pcap = fr_pcap_init(NULL, iface, PCAP_INTERFACE_IN_OUT);
478  if (!pcap) {
479  ERROR("Failed creating pcap");
480  return -1;
481  }
482 
483  if (fr_pcap_open(pcap) < 0) {
484  ERROR("Failed opening interface");
485  talloc_free(pcap);
486  return -1;
487  }
488 
489  fr_inet_ntoh(&request->src_ipaddr, ip, sizeof(ip));
490  sprintf(pcap_filter, "udp and dst port %d", request->src_port);
491 
492  if (fr_pcap_apply_filter(pcap, pcap_filter) < 0) {
493  ERROR("dhcoclient: Failed setting filter for interface");
494  talloc_free(pcap);
495  return -1;
496  }
497 
498  if (fr_dhcp_send_pcap(pcap, eth_bcast, request) < 0) {
499  ERROR("Failed sending packet via PCAP: %s", pcap_geterr(pcap->handle));
500  talloc_free(pcap);
501  return -1;
502  }
503 
504  if (!reply_expected) return 0;
505 
506  *reply = fr_dhcp_recv_raw_loop(pcap->fd,
507 #ifdef HAVE_LINUX_IF_PACKET_H
508  &ll,
509 #endif
510  request);
511 
512  if (!*reply) {
513  ERROR("Error receiving reply");
514  talloc_free(pcap);
515  return -1;
516  }
517 
518  talloc_free(pcap);
519  return 0;
520 }
521 #endif /* HAVE_LIBPCAP */
522 
523 static void dhcp_packet_debug(RADIUS_PACKET *packet, bool received)
524 {
525  vp_cursor_t cursor;
526  char buffer[256];
527 
528  char src_ipaddr[INET6_ADDRSTRLEN];
529  char dst_ipaddr[INET6_ADDRSTRLEN];
530 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
531  char if_name[IFNAMSIZ];
532 #endif
533  VALUE_PAIR *vp;
534 
535  if (!packet) return;
536 
537  /*
538  * Client-specific debugging re-prints the input
539  * packet into the client log.
540  *
541  * This really belongs in a utility library
542  */
543  printf("%s %s Id %08x from %s%s%s:%i to %s%s%s:%i "
544 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
545  "%s%s%s"
546 #endif
547  "length %zu\n",
548  received ? "Received" : "Sending",
550  packet->id,
551  packet->src_ipaddr.af == AF_INET6 ? "[" : "",
552  inet_ntop(packet->src_ipaddr.af,
553  &packet->src_ipaddr.ipaddr,
554  src_ipaddr, sizeof(src_ipaddr)),
555  packet->src_ipaddr.af == AF_INET6 ? "]" : "",
556  packet->src_port,
557  packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
558  inet_ntop(packet->dst_ipaddr.af,
559  &packet->dst_ipaddr.ipaddr,
560  dst_ipaddr, sizeof(dst_ipaddr)),
561  packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
562  packet->dst_port,
563 #if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
564  packet->if_index ? "via " : "",
565  packet->if_index ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
566  packet->if_index ? " " : "",
567 #endif
568  packet->data_len);
569 
570  for (vp = fr_cursor_init(&cursor, &packet->vps);
571  vp;
572  vp = fr_cursor_next(&cursor)) {
573  VERIFY_VP(vp);
574 
575  fr_pair_snprint(buffer, sizeof(buffer), vp);
576  printf("\t%s\n", buffer);
577  }
578 }
579 
580 int main(int argc, char **argv)
581 {
582 
583  static uint16_t server_port = 0;
584  static int packet_code = 0;
585  static fr_ipaddr_t server_ipaddr;
586  static fr_ipaddr_t client_ipaddr;
587 
588  int c;
589  char const *radius_dir = RADDBDIR;
590  char const *dict_dir = DICTDIR;
591  char const *filename = NULL;
592  fr_dict_attr_t const *da;
593  fr_dict_t *dict = NULL;
594 
595  RADIUS_PACKET *request = NULL;
596  RADIUS_PACKET *reply = NULL;
597 
598  int ret;
599 
600  fr_debug_lvl = 1;
601  fr_log_fp = stdout;
602 
603  while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != EOF) switch(c) {
604  case 'D':
605  dict_dir = optarg;
606  break;
607 
608  case 'd':
609  radius_dir = optarg;
610  break;
611 
612  case 'f':
613  filename = optarg;
614  break;
615 
616  case 'i':
617  iface = optarg;
618  break;
619 
620  case 'r':
621  if (!isdigit((int) *optarg)) usage();
622  retries = atoi(optarg);
623  if ((retries == 0) || (retries > 1000)) usage();
624  break;
625 
626  case 't':
627  if (!isdigit((int) *optarg)) usage();
628  timeout = atof(optarg);
629  break;
630 
631  case 'v':
632  DEBUG("%s", dhcpclient_version);
633  exit(0);
634 
635  case 'x':
636  fr_debug_lvl++;
637  break;
638 
639  case 'h':
640  default:
641  usage();
642  }
643  argc -= (optind - 1);
644  argv += (optind - 1);
645 
646  if (argc < 2) usage();
647 
648  /* convert timeout to a struct timeval */
649 #define USEC 1000000
650  tv_timeout.tv_sec = timeout;
651  tv_timeout.tv_usec = ((timeout - (float) tv_timeout.tv_sec) * USEC);
652 
653  if (fr_dict_init(NULL, &dict, dict_dir, RADIUS_DICTIONARY, "radius") < 0) {
654  fr_perror("dhcpclient");
655  exit(1);
656  }
657 
658  if (fr_dict_read(dict, radius_dir, RADIUS_DICTIONARY) == -1) {
659  fr_perror("dhcpclient");
660  exit(1);
661  }
662  fr_strerror(); /* Clear the error buffer */
663 
664  /*
665  * Ensure that dictionary.dhcp is loaded.
666  */
667  da = fr_dict_attr_by_name(NULL, "DHCP-Message-Type");
668  if (!da) {
669  if (fr_dict_read(dict, dict_dir, "dictionary.dhcp") < 0) {
670  ERROR("Failed reading dictionary.dhcp");
671  exit(1);
672  }
673  }
674 
675  /*
676  * Resolve hostname.
677  */
678  server_ipaddr.af = AF_INET;
679  if (strcmp(argv[1], "-") != 0) {
680  if (fr_inet_pton_port(&server_ipaddr, &server_port, argv[1],
681  strlen(argv[1]), AF_UNSPEC, true, true) < 0) {
682  fr_perror("dhcpclient");
683  fr_exit_now(1);
684  }
685  client_ipaddr.af = server_ipaddr.af;
686  }
687 
688  /*
689  * See what kind of request we want to send.
690  */
691  if (argc >= 3) {
692  if (!isdigit((int) argv[2][0])) {
693  packet_code = fr_str2int(request_types, argv[2], -2);
694  if (packet_code == -2) {
695  ERROR("Unknown packet type: %s", argv[2]);
696  usage();
697  }
698  } else {
699  packet_code = atoi(argv[2]);
700  }
701  }
702  if (!server_port) server_port = 67;
703 
704  /*
705  * set "raw mode" if an interface is specified and if destination
706  * IP address is the broadcast address.
707  */
708  if (iface) {
709  iface_ind = if_nametoindex(iface);
710  if (iface_ind <= 0) {
711  ERROR("Unknown interface: %s", iface);
712  exit(1);
713  }
714 
715  if (server_ipaddr.ipaddr.ip4addr.s_addr == 0xFFFFFFFF) {
716  ERROR("Using interface: %s (index: %d) in raw packet mode", iface, iface_ind);
717  raw_mode = true;
718  }
719  }
720 
721  request = request_init(filename);
722  if (!request || !request->vps) {
723  ERROR("Nothing to send");
724  exit(1);
725  }
726 
727  /*
728  * Set defaults if they weren't specified via pairs
729  */
730  if (request->src_port == 0) request->src_port = server_port + 1;
731  if (request->dst_port == 0) request->dst_port = server_port;
732  if (request->src_ipaddr.af == AF_UNSPEC) request->src_ipaddr = client_ipaddr;
733  if (request->dst_ipaddr.af == AF_UNSPEC) request->dst_ipaddr = server_ipaddr;
734  if (!request->code) request->code = packet_code;
735 
736  /*
737  * Sanity check.
738  */
739  if (!request->code) {
740  ERROR("Command was %s, and request did not contain DHCP-Message-Type nor Packet-Type",
741  (argc >= 3) ? "'auto'" : "unspecified");
742  exit(1);
743  }
744 
745  /*
746  * These kind of packets do not get a reply, so don't wait for one.
747  */
748  if ((request->code == PW_DHCP_RELEASE) || (request->code == PW_DHCP_DECLINE)) {
749  reply_expected = false;
750  }
751 
752  /*
753  * Encode the packet
754  */
755  if (fr_dhcp_encode(request) < 0) {
756  ERROR("Failed encoding packet");
757  exit(1);
758  }
759 
760  /*
761  * Decode to produce VALUE_PAIRs from the default field
762  */
763  if (fr_debug_lvl) {
764  fr_dhcp_decode(request);
765  dhcp_packet_debug(request, false);
766  }
767 
768 #ifdef HAVE_LIBPCAP
769  if (raw_mode) {
770  ret = send_with_pcap(&reply, request);
771  } else
772 #endif
773  {
774  ret = send_with_socket(&reply, request);
775  }
776 
777  if (reply) {
778  if (fr_dhcp_decode(reply) < 0) {
779  ERROR("Failed decoding packet");
780  ret = -1;
781  }
782  dhcp_packet_debug(reply, true);
783  }
784  talloc_free(dict);
785 
786  return ret < 0 ? 1 : 0;
787 }
788 
789 #endif /* WITH_DHCP */
static char const * dict_dir
Definition: radwho.c:53
int sockfd
Socket this packet was read from.
Definition: libradius.h:147
FILE * fr_log_fp
Definition: radius.c:81
int id
Packet ID (used to link requests/responses).
Definition: libradius.h:154
static char const * radius_dir
Path to raddb directory.
Definition: mainconfig.c:88
int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
Definition: dict.c:2291
Dictionary attribute.
Definition: dict.h:77
uint32_t server_addr
Definition: dhcpclient.c:90
fr_ipaddr_t src_ipaddr
Src IP address of packet.
Definition: libradius.h:149
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
Definition: inet.h:47
int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port)
Definition: inet.c:632
static int send_with_socket(RADIUS_PACKET **reply, RADIUS_PACKET *request)
Definition: dhcpclient.c:401
#define VERIFY_VP(_x)
Definition: pair.h:44
char const * dhcp_header_names[]
Definition: dhcp.c:121
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
Definition: missing.c:538
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
#define DHCP_CHADDR_LEN
Definition: dhcpclient.c:78
static const FR_NAME_NUMBER request_types[]
Definition: dhcpclient.c:94
static int retries
Definition: dhcpclient.c:55
uint8_t * data
Packet data (body).
Definition: libradius.h:160
uint16_t dst_port
DST Port of packet.
Definition: libradius.h:152
static RADIUS_PACKET * request_init(char const *filename)
Definition: dhcpclient.c:126
uint16_t src_port
Src port of packet.
Definition: libradius.h:151
fr_ipaddr_t dst_ipaddr
Dst IP address of packet.
Definition: libradius.h:150
int fr_debug_lvl
Definition: misc.c:40
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
int main(int argc, char **argv)
Definition: dhcpclient.c:580
static int dhcp_header_sizes[]
Definition: dhcpclient.c:225
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
int af
Address family.
Definition: inet.h:42
Vendors and attribute names.
Definition: dict.c:61
int fr_dhcp_encode(RADIUS_PACKET *packet)
Definition: dhcp.c:1629
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
#define USEC
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
static bool reply_expected
Definition: dhcpclient.c:76
#define STRINGIFY(x)
Definition: build.h:34
RADIUS_PACKET * fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new RADIUS_PACKET.
Definition: radius.c:1651
void void fr_perror(char const *,...) CC_HINT(format(printf
unsigned int attr
Attribute number.
Definition: dict.h:79
union fr_ipaddr_t::@1 ipaddr
unsigned int code
Packet code (type).
Definition: libradius.h:155
int fr_dhcp_decode(RADIUS_PACKET *packet)
Definition: dhcp.c:1103
#define RADIUS_DICTIONARY
Definition: conf.h:7
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
static void NEVER_RETURNS usage(void)
Definition: dhcpclient.c:105
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
Packet code has not been set.
Definition: radius.h:91
int if_index
Index of receiving interface.
Definition: libradius.h:148
static fr_ipaddr_t client_ipaddr
Definition: radclient.c:56
#define TAG_ANY
Definition: pair.h:191
static fr_ipaddr_t server_ipaddr
Definition: radclient.c:51
uint32_t offered_addr
Definition: dhcpclient.c:91
static char * iface
Definition: dhcpclient.c:66
char const * dhcp_message_types[]
Definition: dhcp.c:140
int fr_socket(fr_ipaddr_t *ipaddr, uint16_t port)
Definition: packet.c:136
int fr_dhcp_send_socket(RADIUS_PACKET *packet)
Send DHCP packet using socket.
Definition: dhcp.c:561
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone)
Definition: pair.c:1333
void fr_radius_free(RADIUS_PACKET **)
Free a RADIUS_PACKET.
Definition: radius.c:1727
static int iface_ind
Definition: dhcpclient.c:67
size_t data_len
Length of packet data.
Definition: libradius.h:161
struct dc_offer dc_offer_t
static void print_hex(RADIUS_PACKET *packet)
Definition: dhcpclient.c:235
static float timeout
Definition: dhcpclient.c:56
#define fr_exit_now(_x)
Definition: libradius.h:511
static int sockfd
Definition: dhcpclient.c:59
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
static char const * dhcpclient_version
Definition: dhcpclient.c:82
#define DHCP_MAGIC_VENDOR
Definition: dhcp.h:97
size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
Print one attribute and value to a string.
Definition: pair.c:2189
static bool filedone
Definition: unittest.c:46
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
RADIUS_PACKET * fr_dhcp_recv_socket(int sockfd)
Receive DHCP packet using socket.
Definition: dhcp.c:255
IPv4/6 prefix.
Definition: inet.h:41
#define DHCP_SNAME_LEN
Definition: dhcpclient.c:79
static int packet_code
Definition: radclient.c:50
static void dhcp_packet_debug(RADIUS_PACKET *packet, bool received)
Definition: dhcpclient.c:523
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
#define DEBUG(fmt,...)
Definition: dhcpclient.c:36
#define NEVER_RETURNS
Definition: libradius.h:133
#define DHCP_FILE_LEN
Definition: dhcpclient.c:80
static struct timeval tv_timeout
Definition: dhcpclient.c:57
#define RCSID(id)
Definition: build.h:135
char const * fr_inet_ntoh(fr_ipaddr_t const *src, char *out, size_t outlen)
Perform reverse resolution of an IP address.
Definition: inet.c:226
#define PW_DHCP_MESSAGE_TYPE
Definition: dhcp.h:104
static bool raw_mode
Definition: dhcpclient.c:74
#define PW_DHCP_OFFSET
Definition: dhcp.h:73
int fr_dict_init(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *fn, char const *name)
(re)initialize a protocol dictionary
Definition: dict.c:2148
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_t *dict, char const *attr)
Locate a fr_dict_attr_t by its name.
Definition: dict.c:3493
static uint16_t server_port
Definition: radclient.c:49