25 RCSID(
"$Id: 85d39e47a693423d191a7e49812aee21dedfbbd5 $")
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>
39 #define DEBUG(fmt, ...) if (fr_debug_lvl > 0) fprintf(stdout, fmt "\n", ## __VA_ARGS__)
41 #define ERROR(fmt, ...) fr_perror("dhcpclient: " fmt, ## __VA_ARGS__)
58 static fr_pcap_t *pcap;
64 #ifdef HAVE_LINUX_IF_PACKET_H
65 static struct sockaddr_ll ll;
116 DEBUG(
"Usage: dhcpclient [options] server[:port] [<command>]");
117 DEBUG(
"Send a DHCP request with provided RADIUS attrs and output response.");
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.");
147 fp = fopen(filename,
"r");
153 DEBUG(
"Reading packets from stdin\n");
165 if (fp != stdin) fclose(fp);
179 packet->
code =
vp->vp_uint8;
187 packet->
code =
vp->vp_uint32;
199 if (fp != stdin) fclose(fp);
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,
222 #ifndef HAVE_LINUX_IF_PACKET_H
245 if ((!found) || (reply)) {
246 DEBUG(
"Waiting for %s DHCP replies for: %.6f",
252 FD_SET(lsockfd, &read_fd);
259 if (retval > 0 && FD_ISSET(lsockfd, &read_fd)) {
262 #ifdef HAVE_LINUX_IF_PACKET_H
263 reply = fr_dhcpv4_raw_packet_recv(lsockfd, p_ll, request, request_list);
266 reply = fr_dhcpv4_pcap_recv(pcap);
268 # error Need <if/packet.h> or <pcap.h>
282 ERROR(
"Failed decoding reply");
286 if (!found) found = reply;
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;
303 DEBUG(
"No valid DHCP reply received");
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];
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))
330 #ifndef HAVE_LINUX_IF_PACKET_H
337 #ifdef HAVE_LINUX_IF_PACKET_H
341 ERROR(
"Error opening socket");
364 if (setsockopt(
sockfd, SOL_SOCKET, SO_RCVTIMEO,
370 if (setsockopt(
sockfd, SOL_SOCKET, SO_BROADCAST, &on,
sizeof(on)) < 0) {
376 #ifdef HAVE_LINUX_IF_PACKET_H
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));
384 *reply = fr_dhcpv4_recv_raw_loop(
sockfd, &ll, request, request_list);
386 ERROR(
"Error receiving reply (fr_dhcpv4_recv_raw_loop)");
400 if (errno == EAGAIN) {
402 ERROR(
"Timed out waiting for reply");
404 ERROR(
"Error receiving reply");
417 char pcap_filter[255];
419 pcap = fr_pcap_init(NULL,
iface, PCAP_INTERFACE_IN_OUT);
421 ERROR(
"Failed creating pcap");
425 if (fr_pcap_open(pcap) < 0) {
426 ERROR(
"Failed opening interface");
432 snprintf(pcap_filter,
sizeof(pcap_filter),
"udp and dst port %d", request->
socket.inet.src_port);
434 if (fr_pcap_apply_filter(pcap, pcap_filter) < 0) {
435 ERROR(
"Failing setting filter");
440 if (fr_dhcpv4_pcap_send(pcap,
eth_bcast, request) < 0) {
441 ERROR(
"Failed sending request");
448 *reply = fr_dhcpv4_recv_raw_loop(pcap->fd,
449 #ifdef HAVE_LINUX_IF_PACKET_H
452 request, request_list);
455 ERROR(
"Error receiving reply");
469 char src_ipaddr[INET6_ADDRSTRLEN];
470 char dst_ipaddr[INET6_ADDRSTRLEN];
471 #if defined(WITH_IFINDEX_NAME_RESOLUTION)
472 char if_name[IFNAMSIZ];
484 printf(
"%s %s Id %08x from %s%s%s:%i to %s%s%s:%i "
485 #ifdef WITH_IFINDEX_NAME_RESOLUTION
489 received ?
"Received" :
"Sending",
492 packet->
socket.inet.src_ipaddr.
af == AF_INET6 ?
"[" :
"",
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 ?
"[" :
"",
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 ?
" " :
"",
521 int main(
int argc,
char **argv)
530 char const *raddb_dir = RADDBDIR;
531 char const *dict_dir = DICTDIR;
532 char const *filename = NULL;
551 while ((c = getopt(argc, argv,
"d:D:f:hr:t:vxi:")) != -1)
switch(c) {
590 argc -= (optind - 1);
591 argv += (optind - 1);
593 if (argc < 2)
usage();
631 if (strcmp(argv[1],
"-") != 0) {
633 strlen(argv[1]), AF_UNSPEC,
true,
true) < 0) {
644 if (!isdigit((
uint8_t) argv[2][0])) {
647 ERROR(
"Unknown packet type: %s", argv[2]);
674 ERROR(
"Nothing to send");
691 ERROR(
"Command was %s, and request did not contain Message-Type nor Packet-Type",
692 (argc >= 3) ?
"'auto'" :
"unspecified");
707 ERROR(
"Failed encoding packet");
724 ret = send_with_pcap(&reply, packet, &packet_vps);
733 ERROR(
"Failed decoding packet");
752 return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
static int const char char buffer[256]
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
#define NEVER_RETURNS
Should be placed before the function return type.
#define L(_str)
Helper for initialising arrays of string literals.
#define fr_exit(_x)
Exit, producing a log message in debug builds.
fr_dict_autoload_t dhcpclient_dict[]
static fr_dict_attr_t const * attr_packet_type
static fr_table_num_sorted_t const request_types[]
int main(int argc, char **argv)
static bool reply_expected
static fr_dict_t const * dict_freeradius
static fr_dict_attr_t const * attr_dhcp_message_type
static size_t request_types_len
static fr_dict_t const * dict_dhcpv4
static void dhcp_packet_debug(fr_packet_t *packet, fr_pair_list_t *list, bool received)
static fr_dict_attr_t const * attr_dhcp_your_ip_address
fr_dict_attr_autoload_t dhcpclient_dict_attr[]
static fr_time_delta_t timeout
static fr_dict_attr_t const * attr_dhcp_dhcp_server_identifier
static int request_init(fr_packet_t **out, fr_pair_list_t *packet_vps, char const *filename)
static NEVER_RETURNS void usage(void)
static int send_with_socket(fr_packet_t **reply, fr_packet_t *request, UNUSED fr_pair_list_t *request_list)
static char const * dhcpclient_version
fr_packet_t * fr_dhcpv4_udp_packet_recv(int sockfd)
Receive DHCP packet using a connectionless UDP socket.
int fr_dhcpv4_udp_packet_send(fr_packet_t *packet)
Send DHCP packet using a connectionless UDP socket.
int fr_dhcpv4_packet_encode(fr_packet_t *packet, fr_pair_list_t *list)
int fr_dhcpv4_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, unsigned int *code)
#define fr_dict_autofree(_to_free)
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
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.
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
#define fr_dict_autoload(_to_load)
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.
Specifies an attribute which must be present for the module to function.
Specifies a dictionary which must be loaded/loadable for the module to function.
char const * fr_inet_ntoh(fr_ipaddr_t const *src, char *out, size_t outlen)
Perform reverse resolution of an IP address.
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)
union fr_ipaddr_t::@130 addr
int packet_global_init(void)
Initialises the Net.
void fr_packet_net_from_pairs(fr_packet_t *packet, fr_pair_list_t const *list)
Convert pairs to information in a packet.
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
void fr_packet_free(fr_packet_t **packet_p)
Free a fr_packet_t.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
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.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
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.
char const * dhcp_message_types[]
uint8_t eth_bcast[ETH_ADDR_LEN]
int fr_dhcpv4_global_init(void)
Resolve/cache attributes in the DHCP dictionary.
void fr_dhcpv4_global_free(void)
void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
Print a raw DHCP packet as hex.
static fr_ipaddr_t client_ipaddr
static uint16_t server_port
static fr_ipaddr_t server_ipaddr
#define FR_SBUFF_OUT(_start, _len_or_end)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
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.
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.
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
An element in a lexicographically sorted array of name to num mappings.
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.
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
#define fr_time_delta_wrap(_time)
#define fr_time_delta_ispos(_a)
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
A time delta, a difference in time measured in nanoseconds.
#define FR_DICTIONARY_FILE
unsigned int code
Packet code (type).
fr_socket_t socket
This packet was received on.
int id
Packet ID (used to link requests/responses).
uint8_t * data
Packet data (body).
size_t data_len
Length of packet data.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
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.
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.
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
int af
AF_INET, AF_INET6, or AF_UNIX.
int fd
File descriptor if this is a live socket.
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
char const * fr_strerror(void)
Get the last library error.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define RADIUSD_VERSION_BUILD(_x)
Create a version string for a utility in the suite of FreeRADIUS utilities.
static size_t char ** out