The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
dhcpclient.c
Go to the documentation of this file.
1 /*
2  * dhcpclient.c DHCP test client.
3  *
4  * Version: $Id: 85d39e47a693423d191a7e49812aee21dedfbbd5 $
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@freeradius.org)
23  */
24 
25 RCSID("$Id: 85d39e47a693423d191a7e49812aee21dedfbbd5 $")
26 
27 #include <freeradius-devel/util/conf.h>
28 #include <freeradius-devel/util/syserror.h>
29 #include <freeradius-devel/util/atexit.h>
30 #include <freeradius-devel/util/pair_legacy.h>
31 #include <freeradius-devel/server/packet.h>
32 #include <freeradius-devel/dhcpv4/dhcpv4.h>
33 #include <freeradius-devel/util/pcap.h>
34 
35 /*
36  * Logging macros
37  */
38 #undef DEBUG
39 #define DEBUG(fmt, ...) if (fr_debug_lvl > 0) fprintf(stdout, fmt "\n", ## __VA_ARGS__)
40 
41 #define ERROR(fmt, ...) fr_perror("dhcpclient: " fmt, ## __VA_ARGS__)
42 
43 #include <ctype.h>
44 
45 #ifdef HAVE_GETOPT_H
46 # include <getopt.h>
47 #endif
48 
49 #include <assert.h>
50 
51 #include <net/if.h>
52 
53 static int retries = 3;
55 
56 static int sockfd;
57 #ifdef HAVE_LIBPCAP
58 static fr_pcap_t *pcap;
59 #endif
60 
61 static char *iface = NULL;
62 static int iface_ind = -1;
63 
64 #ifdef HAVE_LINUX_IF_PACKET_H
65 static struct sockaddr_ll ll; /* Socket address structure */
66 #endif
67 
68 static bool raw_mode = false;
69 static bool reply_expected = true;
70 
71 static char const *dhcpclient_version = RADIUSD_VERSION_BUILD("dhcpclient");
72 
73 /* structure to keep track of offered IP addresses */
74 typedef struct {
77 } dc_offer_t;
78 
79 static fr_dict_t const *dict_freeradius;
80 static fr_dict_t const *dict_dhcpv4;
81 
84  { .out = &dict_freeradius, .proto = "freeradius" },
85  { .out = &dict_dhcpv4, .proto = "dhcpv4" },
86  { NULL }
87 };
88 
93 
96  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
97  { .out = &attr_dhcp_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4},
98  { .out = &attr_dhcp_dhcp_server_identifier, .name = "Server-Identifier", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
99  { .out = &attr_dhcp_your_ip_address, .name = "Your-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
100  { NULL }
101 };
102 
104  { L("auto"), 0 },
105  { L("decline"), FR_DHCP_DECLINE },
106  { L("discover"), FR_DHCP_DISCOVER },
107  { L("inform"), FR_DHCP_INFORM },
108  { L("lease_query"), FR_DHCP_LEASE_QUERY },
109  { L("release"), FR_DHCP_RELEASE },
110  { L("request"), FR_DHCP_REQUEST }
111 };
113 
114 static NEVER_RETURNS void usage(void)
115 {
116  DEBUG("Usage: dhcpclient [options] server[:port] [<command>]");
117  DEBUG("Send a DHCP request with provided RADIUS attrs and output response.");
118 
119  DEBUG(" <command> One of: discover, request, decline, release, inform; or: auto.");
120  DEBUG(" -d <directory> Set the directory where the dictionaries are stored (defaults to " RADDBDIR ").");
121  DEBUG(" -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").");
122  DEBUG(" -f <file> Read packets from file, not stdin.");
123  DEBUG(" -i <interface> Use this interface to send/receive at packet level on a raw socket.");
124  DEBUG(" -r <retries> On timeout, retry sending the packet 'retries' times.\n");
125  DEBUG(" -t <timeout> Wait 'timeout' seconds for a reply (may be a floating point number).");
126  DEBUG(" -v Show program version information.");
127  DEBUG(" -x Debugging mode.");
128 
129  fr_exit(EXIT_SUCCESS);
130 }
131 
132 
133 /*
134  * Initialize the request.
135  */
136 static int request_init(fr_packet_t **out, fr_pair_list_t *packet_vps, char const *filename)
137 {
138  FILE *fp;
139  fr_pair_t *vp;
140  bool filedone = false;
141  fr_packet_t *packet;
142 
143  /*
144  * Determine where to read the VP's from.
145  */
146  if (filename) {
147  fp = fopen(filename, "r");
148  if (!fp) {
149  ERROR("Error opening %s: %s", filename, fr_syserror(errno));
150  return -1;
151  }
152  } else {
153  DEBUG("Reading packets from stdin\n");
154  fp = stdin;
155  }
156 
157  packet = fr_packet_alloc(NULL, false);
158 
159  /*
160  * Read the VP's.
161  */
162  if (fr_pair_list_afrom_file(packet, dict_dhcpv4, packet_vps, fp, &filedone) < 0) {
163  fr_perror("dhcpclient");
164  fr_packet_free(&packet);
165  if (fp != stdin) fclose(fp);
166  return -1;
167  }
168 
169  /*
170  * Fix / set various options
171  */
172  for (vp = fr_pair_list_head(packet_vps);
173  vp;
174  vp = fr_pair_list_next(packet_vps, vp)) {
175  /*
176  * Allow to set packet type using Message-Type
177  */
178  if (vp->da == attr_dhcp_message_type) {
179  packet->code = vp->vp_uint8;
180 
181  /*
182  * Allow it to set the packet type in
183  * the attributes read from the file.
184  * (this takes precedence over the command argument.)
185  */
186  } else if (vp->da == attr_packet_type) {
187  packet->code = vp->vp_uint32;
188 
189  } /* switch over the attribute */
190 
191  } /* loop over the VP's we read in */
192 
193  /*
194  * Fill in the packet header from attributes, and then
195  * re-realize the attributes.
196  */
197  fr_packet_net_from_pairs(packet, packet_vps);
198 
199  if (fp != stdin) fclose(fp);
200 
201  /*
202  * And we're done.
203  */
204  *out = packet;
205 
206  return 0;
207 }
208 
209 
210 /*
211  * Loop waiting for DHCP replies until timer expires.
212  * Note that there may be more than one reply: multiple DHCP servers can respond to a broadcast discover.
213  * A real client would pick one of the proposed replies.
214  * We'll just return the first eligible reply, and display the others.
215  */
216 #if defined(HAVE_LINUX_IF_PACKET_H) || defined (HAVE_LIBPCAP)
217 static fr_packet_t *fr_dhcpv4_recv_raw_loop(int lsockfd,
218 #ifdef HAVE_LINUX_IF_PACKET_H
219  struct sockaddr_ll *p_ll,
220 #endif
221  fr_packet_t *request,
222 #ifndef HAVE_LINUX_IF_PACKET_H
223  UNUSED
224 #endif
225  fr_pair_list_t *request_list
226  )
227 {
228  fr_time_delta_t our_timeout;
229  fr_packet_t *found = NULL;
230  fr_packet_t *reply = NULL;
231  fr_pair_list_t reply_vps;
232  int nb_reply = 0;
233  int nb_offer = 0;
234  dc_offer_t *offer_list = NULL;
235  fd_set read_fd;
236  int retval;
237  fr_dcursor_t cursor;
238 
239  fr_pair_list_init(&reply_vps);
240 
241  our_timeout = timeout;
242 
243  /* Loop waiting for DHCP replies until timer expires */
244  while (fr_time_delta_ispos(our_timeout)) {
245  if ((!found) || (reply)) { // only debug at start and each time we get a valid DHCP reply on raw socket
246  DEBUG("Waiting for %s DHCP replies for: %.6f",
247  (nb_reply > 0) ? " additional ":" ", fr_time_delta_unwrap(our_timeout) / (double)NSEC);
248  }
249 
250  reply = NULL;
251  FD_ZERO(&read_fd);
252  FD_SET(lsockfd, &read_fd);
253  retval = select(lsockfd + 1, &read_fd, NULL, NULL, &fr_time_delta_to_timeval(our_timeout));
254  if (retval < 0) {
255  fr_strerror_printf("Select on DHCP socket failed: %s", fr_syserror(errno));
256  return NULL;
257  }
258 
259  if (retval > 0 && FD_ISSET(lsockfd, &read_fd)) {
260  /* There is something to read on our socket */
261 
262 #ifdef HAVE_LINUX_IF_PACKET_H
263  reply = fr_dhcpv4_raw_packet_recv(lsockfd, p_ll, request, request_list);
264 #else
265 # ifdef HAVE_LIBPCAP
266  reply = fr_dhcpv4_pcap_recv(pcap);
267 # else
268 # error Need <if/packet.h> or <pcap.h>
269 # endif
270 #endif
271  } else {
272  our_timeout = fr_time_delta_wrap(0);
273  }
274 
275  if (reply) {
276  nb_reply ++;
277 
278  if (fr_debug_lvl > 1) fr_dhcpv4_print_hex(stdout, reply->data, reply->data_len);
279 
280  fr_pair_dcursor_init(&cursor, &reply_vps);
281  if (fr_dhcpv4_decode(reply, &reply_vps, reply->data, reply->data_len, &reply->code) < 0) {
282  ERROR("Failed decoding reply");
283  return NULL;
284  }
285 
286  if (!found) found = reply;
287 
288  if (reply->code == FR_DHCP_OFFER) {
290  fr_pair_t *vp2 = fr_pair_find_by_da(&reply_vps, NULL, attr_dhcp_your_ip_address);
291 
292  if (vp1 && vp2) {
293  nb_offer++;
294  offer_list = talloc_realloc(request, offer_list, dc_offer_t, nb_offer);
295  offer_list[nb_offer - 1].server_addr = vp1->vp_ipv4addr;
296  offer_list[nb_offer - 1].offered_addr = vp2->vp_ipv4addr;
297  }
298  }
299  }
300  }
301 
302  if (0 == nb_reply) {
303  DEBUG("No valid DHCP reply received");
304  return NULL;
305  }
306 
307  /* display offer(s) received */
308  if (nb_offer > 0 ) {
309  int i;
310 
311  DEBUG("Received %d DHCP Offer(s):", nb_offer);
312  for (i = 0; i < nb_reply; i++) {
313  char server_addr_buf[INET6_ADDRSTRLEN];
314  char offered_addr_buf[INET6_ADDRSTRLEN];
315 
316  DEBUG("IP address: %s offered by DHCP server: %s",
317  inet_ntop(AF_INET, &offer_list[i].offered_addr,
318  offered_addr_buf, sizeof(offered_addr_buf)),
319  inet_ntop(AF_INET, &offer_list[i].server_addr,
320  server_addr_buf, sizeof(server_addr_buf))
321  );
322  }
323  }
324 
325  return found;
326 }
327 #endif /* <if/packet.h> or <pcap.h> */
328 
329 static int send_with_socket(fr_packet_t **reply, fr_packet_t *request,
330 #ifndef HAVE_LINUX_IF_PACKET_H
331  UNUSED
332 #endif
333  fr_pair_list_t *request_list)
334 {
335  int on = 1;
336 
337 #ifdef HAVE_LINUX_IF_PACKET_H
338  if (raw_mode) {
339  sockfd = fr_dhcpv4_raw_socket_open(&ll, iface_ind);
340  if (sockfd < 0) {
341  ERROR("Error opening socket");
342  return -1;
343  }
344  } else
345 #endif
346  {
347  sockfd = fr_socket_server_udp(&request->socket.inet.src_ipaddr, &request->socket.inet.src_port, NULL, false);
348  if (sockfd < 0) {
349  ERROR("Error opening socket - %s", fr_strerror());
350  return -1;
351  }
352 
353  if (fr_socket_bind(sockfd, NULL, &request->socket.inet.src_ipaddr, &request->socket.inet.src_port) < 0) {
354  ERROR("Error binding socket - %s", fr_strerror());
355  return -1;
356  }
357  }
358 
359 
360  /*
361  * Set option 'receive timeout' on socket.
362  * Note: in case of a timeout, the error will be "Resource temporarily unavailable".
363  */
364  if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,
365  &fr_time_delta_to_timeval(timeout), sizeof(struct timeval)) == -1) {
366  ERROR("Failed setting socket timeout: %s", fr_syserror(errno));
367  return -1;
368  }
369 
370  if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
371  ERROR("Can't set broadcast option: %s", fr_syserror(errno));
372  return -1;
373  }
374  request->socket.fd = sockfd;
375 
376 #ifdef HAVE_LINUX_IF_PACKET_H
377  if (raw_mode) {
378  if (fr_dhcpv4_raw_packet_send(sockfd, &ll, request, request_list) < 0) {
379  ERROR("Failed sending (fr_dhcpv4_raw_packet_send): %s", fr_syserror(errno));
380  return -1;
381  }
382  if (!reply_expected) return 0;
383 
384  *reply = fr_dhcpv4_recv_raw_loop(sockfd, &ll, request, request_list);
385  if (!*reply) {
386  ERROR("Error receiving reply (fr_dhcpv4_recv_raw_loop)");
387  return -1;
388  }
389  } else
390 #endif
391  {
392  if (fr_dhcpv4_udp_packet_send(request) < 0) {
393  ERROR("Failed sending: %s", fr_syserror(errno));
394  return -1;
395  }
396  if (!reply_expected) return 0;
397 
399  if (!*reply) {
400  if (errno == EAGAIN) {
401  fr_strerror_clear(); /* clear error */
402  ERROR("Timed out waiting for reply");
403  } else {
404  ERROR("Error receiving reply");
405  }
406  return -1;
407  }
408  }
409 
410  return 0;
411 }
412 
413 #ifdef HAVE_LIBPCAP
414 static int send_with_pcap(fr_packet_t **reply, fr_packet_t *request, fr_pair_list_t *request_list)
415 {
416  char ip[16];
417  char pcap_filter[255];
418 
419  pcap = fr_pcap_init(NULL, iface, PCAP_INTERFACE_IN_OUT);
420  if (!pcap) {
421  ERROR("Failed creating pcap");
422  return -1;
423  }
424 
425  if (fr_pcap_open(pcap) < 0) {
426  ERROR("Failed opening interface");
427  talloc_free(pcap);
428  return -1;
429  }
430 
431  fr_inet_ntoh(&request->socket.inet.src_ipaddr, ip, sizeof(ip));
432  snprintf(pcap_filter, sizeof(pcap_filter), "udp and dst port %d", request->socket.inet.src_port);
433 
434  if (fr_pcap_apply_filter(pcap, pcap_filter) < 0) {
435  ERROR("Failing setting filter");
436  talloc_free(pcap);
437  return -1;
438  }
439 
440  if (fr_dhcpv4_pcap_send(pcap, eth_bcast, request) < 0) {
441  ERROR("Failed sending request");
442  talloc_free(pcap);
443  return -1;
444  }
445 
446  if (!reply_expected) return 0;
447 
448  *reply = fr_dhcpv4_recv_raw_loop(pcap->fd,
449 #ifdef HAVE_LINUX_IF_PACKET_H
450  &ll,
451 #endif
452  request, request_list);
453 
454  if (!*reply) {
455  ERROR("Error receiving reply");
456  talloc_free(pcap);
457  return -1;
458  }
459 
460  talloc_free(pcap);
461  return 0;
462 }
463 #endif /* HAVE_LIBPCAP */
464 
465 static void dhcp_packet_debug(fr_packet_t *packet, fr_pair_list_t *list, bool received)
466 {
467  char buffer[2048];
468 
469  char src_ipaddr[INET6_ADDRSTRLEN];
470  char dst_ipaddr[INET6_ADDRSTRLEN];
471 #if defined(WITH_IFINDEX_NAME_RESOLUTION)
472  char if_name[IFNAMSIZ];
473 #endif
474  fr_pair_t *vp;
475 
476  if (!packet) return;
477 
478  /*
479  * Client-specific debugging re-prints the input
480  * packet into the client log.
481  *
482  * This really belongs in a utility library
483  */
484  printf("%s %s Id %08x from %s%s%s:%i to %s%s%s:%i "
485 #ifdef WITH_IFINDEX_NAME_RESOLUTION
486  "%s%s%s"
487 #endif
488  "length %zu\n",
489  received ? "Received" : "Sending",
490  dhcp_message_types[packet->code],
491  packet->id,
492  packet->socket.inet.src_ipaddr.af == AF_INET6 ? "[" : "",
493  inet_ntop(packet->socket.inet.src_ipaddr.af,
494  &packet->socket.inet.src_ipaddr.addr,
495  src_ipaddr, sizeof(src_ipaddr)),
496  packet->socket.inet.src_ipaddr.af == AF_INET6 ? "]" : "",
497  packet->socket.inet.src_port,
498  packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "[" : "",
499  inet_ntop(packet->socket.inet.dst_ipaddr.af,
500  &packet->socket.inet.dst_ipaddr.addr,
501  dst_ipaddr, sizeof(dst_ipaddr)),
502  packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "]" : "",
503  packet->socket.inet.dst_port,
504 #ifdef WITH_IFINDEX_NAME_RESOLUTION
505  packet->socket.inet.ifindex ? "via " : "",
506  packet->socket.inet.ifindex ? fr_ifname_from_ifindex(if_name, packet->socket.inet.ifindex) : "",
507  packet->socket.inet.ifindex ? " " : "",
508 #endif
509  packet->data_len);
510 
511  for (vp = fr_pair_list_head(list);
512  vp;
513  vp = fr_pair_list_next(list, vp)) {
514  PAIR_VERIFY(vp);
515 
516  (void) fr_pair_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), NULL, vp);
517  printf("\t%s\n", buffer);
518  }
519 }
520 
521 int main(int argc, char **argv)
522 {
523 
524  static uint16_t server_port = 0;
525  static int packet_code = 0;
526  static fr_ipaddr_t server_ipaddr;
527  static fr_ipaddr_t client_ipaddr;
528 
529  int c;
530  char const *raddb_dir = RADDBDIR;
531  char const *dict_dir = DICTDIR;
532  char const *filename = NULL;
533 
534  fr_packet_t *packet = NULL;
535  fr_pair_list_t packet_vps;
536  fr_packet_t *reply = NULL;
537  fr_pair_list_t reply_vps;
538 
539  int ret;
540 
541  fr_pair_list_init(&packet_vps);
542  fr_pair_list_init(&reply_vps);
543 
544  /*
545  * Must be called first, so the handler is called last
546  */
548 
549  fr_debug_lvl = 1;
550 
551  while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != -1) switch(c) {
552  case 'D':
553  dict_dir = optarg;
554  break;
555 
556  case 'd':
557  raddb_dir = optarg;
558  break;
559 
560  case 'f':
561  filename = optarg;
562  break;
563 
564  case 'i':
565  iface = optarg;
566  break;
567 
568  case 'r':
569  if (!isdigit((uint8_t) *optarg)) usage();
570  retries = atoi(optarg);
571  if ((retries == 0) || (retries > 1000)) usage();
572  break;
573 
574  case 't':
575  if (fr_time_delta_from_str(&timeout, optarg, strlen(optarg), FR_TIME_RES_SEC) < 0) usage();
576  break;
577 
578  case 'v':
579  DEBUG("%s", dhcpclient_version);
580  fr_exit(EXIT_SUCCESS);
581 
582  case 'x':
583  fr_debug_lvl++;
584  break;
585 
586  case 'h':
587  default:
588  usage();
589  }
590  argc -= (optind - 1);
591  argv += (optind - 1);
592 
593  if (argc < 2) usage();
594 
595  if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
596  fr_perror("dhcpclient");
597  fr_exit(EXIT_FAILURE);
598  }
599 
601  fr_perror("dhcpclient");
602  fr_exit(EXIT_FAILURE);
603  }
604 
606  fr_perror("dhcpclient");
607  fr_exit(EXIT_FAILURE);
608  }
609 
611  fr_perror("dhcpclient");
612  fr_exit(EXIT_FAILURE);
613  }
614 
616 
617  fr_strerror_clear(); /* Clear the error buffer */
618 
619  /*
620  * Initialise the DHCPv4 library
621  */
622  if (fr_dhcpv4_global_init() < 0) {
623  fr_perror("dhcpclient");
624  fr_exit(EXIT_FAILURE);
625  }
626 
627  /*
628  * Resolve hostname.
629  */
630  server_ipaddr.af = AF_INET;
631  if (strcmp(argv[1], "-") != 0) {
633  strlen(argv[1]), AF_UNSPEC, true, true) < 0) {
634  fr_perror("dhcpclient");
635  fr_exit(EXIT_FAILURE);
636  }
638  }
639 
640  /*
641  * See what kind of request we want to send.
642  */
643  if (argc >= 3) {
644  if (!isdigit((uint8_t) argv[2][0])) {
646  if (packet_code == -2) {
647  ERROR("Unknown packet type: %s", argv[2]);
648  usage();
649  }
650  } else {
651  packet_code = atoi(argv[2]);
652  }
653  }
654  if (!server_port) server_port = 67;
655 
656  /*
657  * set "raw mode" if an interface is specified and if destination
658  * IP address is the broadcast address.
659  */
660  if (iface) {
661  iface_ind = if_nametoindex(iface);
662  if (iface_ind <= 0) {
663  ERROR("Unknown interface: %s", iface);
664  fr_exit(EXIT_FAILURE);
665  }
666 
667  if (server_ipaddr.addr.v4.s_addr == 0xFFFFFFFF) {
668  ERROR("Using interface: %s (index: %d) in raw packet mode", iface, iface_ind);
669  raw_mode = true;
670  }
671  }
672 
673  if ((request_init(&packet, &packet_vps, filename) < 0) || fr_pair_list_empty(&packet_vps)) {
674  ERROR("Nothing to send");
675  fr_exit(EXIT_FAILURE);
676  }
677 
678  /*
679  * Set defaults if they weren't specified via pairs
680  */
681  if (packet->socket.inet.src_port == 0) packet->socket.inet.src_port = server_port + 1;
682  if (packet->socket.inet.dst_port == 0) packet->socket.inet.dst_port = server_port;
683  if (packet->socket.inet.src_ipaddr.af == AF_UNSPEC) packet->socket.inet.src_ipaddr = client_ipaddr;
684  if (packet->socket.inet.dst_ipaddr.af == AF_UNSPEC) packet->socket.inet.dst_ipaddr = server_ipaddr;
685  if (!packet->code) packet->code = packet_code;
686 
687  /*
688  * Sanity check.
689  */
690  if (!packet->code) {
691  ERROR("Command was %s, and request did not contain Message-Type nor Packet-Type",
692  (argc >= 3) ? "'auto'" : "unspecified");
693  fr_exit(EXIT_FAILURE);
694  }
695 
696  /*
697  * These kind of packets do not get a reply, so don't wait for one.
698  */
699  if ((packet->code == FR_DHCP_RELEASE) || (packet->code == FR_DHCP_DECLINE)) {
700  reply_expected = false;
701  }
702 
703  /*
704  * Encode the packet
705  */
706  if (fr_dhcpv4_packet_encode(packet, &packet_vps) < 0) {
707  ERROR("Failed encoding packet");
708  fr_exit(EXIT_FAILURE);
709  }
710 
711  /*
712  * Decode the request to produce fr_pair_t's from the headers.
713  */
714  if (fr_debug_lvl) {
715  if (fr_debug_lvl > 1) fr_dhcpv4_print_hex(stdout, packet->data, packet->data_len);
716 
717  (void) fr_dhcpv4_decode(packet, &reply_vps, packet->data, packet->data_len, &packet->code);
718  dhcp_packet_debug(packet, &reply_vps, false);
719  fr_pair_list_free(&reply_vps);
720  }
721 
722 #ifdef HAVE_LIBPCAP
723  if (raw_mode) {
724  ret = send_with_pcap(&reply, packet, &packet_vps);
725  } else
726 #endif
727  {
728  ret = send_with_socket(&reply, packet, &packet_vps);
729  }
730 
731  if (reply) {
732  if (fr_dhcpv4_decode(reply, &reply_vps, reply->data, reply->data_len, &reply->code) < 0) {
733  ERROR("Failed decoding packet");
734  ret = -1;
735  }
736  dhcp_packet_debug(reply, &reply_vps, true);
737  }
738 
740 
742  fr_perror("dhcpclient");
743  ret = -1;
744  }
745 
746  /*
747  * Ensure our atexit handlers run before any other
748  * atexit handlers registered by third party libraries.
749  */
751 
752  return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
753 }
static int const char char buffer[256]
Definition: acutest.h:574
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition: atexit.c:160
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition: atexit.c:286
#define RCSID(id)
Definition: build.h:481
#define NEVER_RETURNS
Should be placed before the function return type.
Definition: build.h:311
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition: debug.h:228
fr_dict_autoload_t dhcpclient_dict[]
Definition: dhcpclient.c:83
static fr_dict_attr_t const * attr_packet_type
Definition: dhcpclient.c:89
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_table_num_sorted_t const request_types[]
Definition: dhcpclient.c:103
int main(int argc, char **argv)
Definition: dhcpclient.c:521
static bool reply_expected
Definition: dhcpclient.c:69
static fr_dict_t const * dict_freeradius
Definition: dhcpclient.c:79
static fr_dict_attr_t const * attr_dhcp_message_type
Definition: dhcpclient.c:90
static int iface_ind
Definition: dhcpclient.c:62
static size_t request_types_len
Definition: dhcpclient.c:112
static fr_dict_t const * dict_dhcpv4
Definition: dhcpclient.c:80
static int retries
Definition: dhcpclient.c:53
uint32_t server_addr
Definition: dhcpclient.c:75
static void dhcp_packet_debug(fr_packet_t *packet, fr_pair_list_t *list, bool received)
Definition: dhcpclient.c:465
static fr_dict_attr_t const * attr_dhcp_your_ip_address
Definition: dhcpclient.c:92
static bool raw_mode
Definition: dhcpclient.c:68
fr_dict_attr_autoload_t dhcpclient_dict_attr[]
Definition: dhcpclient.c:95
static int sockfd
Definition: dhcpclient.c:56
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
static fr_dict_attr_t const * attr_dhcp_dhcp_server_identifier
Definition: dhcpclient.c:91
static int request_init(fr_packet_t **out, fr_pair_list_t *packet_vps, char const *filename)
Definition: dhcpclient.c:136
static NEVER_RETURNS void usage(void)
Definition: dhcpclient.c:114
static int send_with_socket(fr_packet_t **reply, fr_packet_t *request, UNUSED fr_pair_list_t *request_list)
Definition: dhcpclient.c:329
static char const * dhcpclient_version
Definition: dhcpclient.c:71
uint32_t offered_addr
Definition: dhcpclient.c:76
static char * iface
Definition: dhcpclient.c:61
fr_packet_t * fr_dhcpv4_udp_packet_recv(int sockfd)
Receive DHCP packet using a connectionless UDP socket.
Definition: udp.c:74
int fr_dhcpv4_udp_packet_send(fr_packet_t *packet)
Send DHCP packet using a connectionless UDP socket.
Definition: udp.c:41
@ FR_DHCP_REQUEST
Definition: dhcpv4.h:47
@ FR_DHCP_DECLINE
Definition: dhcpv4.h:48
@ FR_DHCP_OFFER
Definition: dhcpv4.h:46
@ FR_DHCP_DISCOVER
Definition: dhcpv4.h:45
@ FR_DHCP_LEASE_QUERY
Definition: dhcpv4.h:54
@ FR_DHCP_RELEASE
Definition: dhcpv4.h:51
@ FR_DHCP_INFORM
Definition: dhcpv4.h:52
int fr_dhcpv4_packet_encode(fr_packet_t *packet, fr_pair_list_t *list)
Definition: packet.c:367
int fr_dhcpv4_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, unsigned int *code)
Definition: packet.c:100
#define fr_dict_autofree(_to_free)
Definition: dict.h:850
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition: dict_util.c:4090
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition: dict_util.c:4585
#define fr_dict_autoload(_to_load)
Definition: dict.h:847
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename)
Read supplementary attribute definitions into an existing dictionary.
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir)
Initialise the global protocol hashes.
Definition: dict_util.c:4392
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
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:355
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:937
int af
Address family.
Definition: inet.h:64
union fr_ipaddr_t::@130 addr
IPv4/6 prefix.
Definition: merged_model.c:272
int packet_global_init(void)
Initialises the Net.
Definition: packet.c:185
void fr_packet_net_from_pairs(fr_packet_t *packet, fr_pair_list_t const *list)
Convert pairs to information in a packet.
Definition: packet.c:139
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:43
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition: packet.c:38
void fr_packet_free(fr_packet_t **packet_p)
Free a fr_packet_t.
Definition: packet.c:89
unsigned short uint16_t
Definition: merged_model.c:31
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
unsigned int uint32_t
Definition: merged_model.c:33
unsigned char uint8_t
Definition: merged_model.c:30
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
Definition: missing.c:443
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
int fr_pair_list_afrom_file(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_pair_list_t *out, FILE *fp, bool *pfiledone)
Read valuepairs from the fp up to End-Of-File.
Definition: pair_legacy.c:648
char const * dhcp_message_types[]
Definition: base.c:126
uint8_t eth_bcast[ETH_ADDR_LEN]
Definition: base.c:164
int fr_dhcpv4_global_init(void)
Resolve/cache attributes in the DHCP dictionary.
Definition: base.c:584
void fr_dhcpv4_global_free(void)
Definition: base.c:628
void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
Print a raw DHCP packet as hex.
Definition: base.c:672
static int packet_code
Definition: radclient-ng.c:76
static fr_ipaddr_t client_ipaddr
Definition: radclient.c:83
static uint16_t server_port
Definition: radclient.c:76
static fr_ipaddr_t server_ipaddr
Definition: radclient.c:78
#define FR_SBUFF_OUT(_start, _len_or_end)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
int fr_socket_server_udp(fr_ipaddr_t const *src_ipaddr, uint16_t *src_port, char const *port_name, bool async)
Open an IPv4/IPv6 unconnected UDP socket.
Definition: socket.c:867
int fr_socket_bind(int sockfd, char const *ifname, fr_ipaddr_t *src_ipaddr, uint16_t *src_port)
Bind a UDP/TCP v4/v6 socket to a given ipaddr src port, and interface.
Definition: socket.c:229
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition: table.h:653
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:49
fr_slen_t fr_time_delta_from_str(fr_time_delta_t *out, char const *in, size_t inlen, fr_time_res_t hint)
Create fr_time_delta_t from a string.
Definition: time.c:445
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition: time.h:154
#define fr_time_delta_wrap(_time)
Definition: time.h:152
#define fr_time_delta_ispos(_a)
Definition: time.h:290
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
Definition: time.h:656
@ FR_TIME_RES_SEC
Definition: time.h:50
#define NSEC
Definition: time.h:379
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
static bool filedone
#define FR_DICTIONARY_FILE
Definition: conf.h:7
unsigned int code
Packet code (type).
Definition: packet.h:61
fr_socket_t socket
This packet was received on.
Definition: packet.h:57
int id
Packet ID (used to link requests/responses).
Definition: packet.h:60
uint8_t * data
Packet data (body).
Definition: packet.h:63
size_t data_len
Length of packet data.
Definition: packet.h:64
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition: pair_inline.c:43
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition: pair_inline.c:70
#define PAIR_VERIFY(_x)
Definition: pair.h:191
static fr_slen_t quote ssize_t fr_pair_print(fr_sbuff_t *out, fr_dict_attr_t const *parent, fr_pair_t const *vp))
Print one attribute and value to a string.
Definition: pair_print.c:104
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:591
int af
AF_INET, AF_INET6, or AF_UNIX.
Definition: socket.h:78
int fd
File descriptor if this is a live socket.
Definition: socket.h:81
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition: strerror.c:733
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define RADIUSD_VERSION_BUILD(_x)
Create a version string for a utility in the suite of FreeRADIUS utilities.
Definition: version.h:58
static size_t char ** out
Definition: value.h:997