We now do ARP checking by default as recommended by RFC 2131.

Add RFC 3927 (aka IPV4LL aka APIPA) support by default.
This commit is contained in:
Roy Marples 2007-07-18 11:26:59 +00:00
parent ff711cf766
commit 77659ce035
13 changed files with 330 additions and 103 deletions

View File

@ -1,3 +1,5 @@
We now do ARP checking by default as recommended by RFC 2131.
Add RFC 3927 (aka IPV4LL aka APIPA) support by default.
Suport DHCP option (52) overload.
Added -T test option. This just sends a DHCP_DISCOVER message and then
prints the configuration data to stdout - we don't request a lease,

View File

@ -1,4 +1,4 @@
VERSION = 3.1.0_pre4
VERSION = 3.1.0_pre5
CFLAGS ?= -O2 -pipe
# Should work for both GNU make and BSD make
@ -47,7 +47,7 @@ TARGET = $(SBIN_TARGETS)
dhcpcd_H = version.h
dhcpcd_OBJS = arp.o client.o common.o configure.o dhcp.o dhcpcd.o duid.o \
info.o interface.o logger.o signals.o socket.o
info.o interface.o ipv4ll.o logger.o signals.o socket.o
# By default we don't need to link to anything
# Except on Darwin where we need -lresolv, so they need to uncomment this

95
arp.c
View File

@ -31,6 +31,7 @@
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -42,8 +43,11 @@
#include "logger.h"
#include "socket.h"
/* Longer is safer and slower - 2 seconds seems a happy medium */
#define TIMEOUT 2
/* These are really for IPV4LL */
#define NPROBES 3
#define PROBE_INTERVAL 200000
#define NCLAIMS 2
#define CLAIM_INTERVAL 200000
/* Linux does not seem to define these handy macros */
#ifndef ar_sha
@ -64,22 +68,14 @@
#define IP_MIN_FRAME_LENGTH 46
#ifdef ENABLE_ARP
int arp_check (interface_t *iface, struct in_addr address)
static int send_arp (interface_t *iface, int op, struct in_addr sip,
unsigned char *taddr, struct in_addr tip)
{
struct arphdr *arp;
struct arphdr *reply = NULL;
long timeout = 0;
unsigned char *buffer;
int arpsize = arphdr_len2 (iface->hwlen, sizeof (struct in_addr));
int retval = 0;
int retval;
if (! iface->arpable) {
logger (LOG_DEBUG, "arp_check: interface `%s' is not ARPable",
iface->name);
return (0);
}
inet_aton ("192.168.0.3", &address);
arp = xmalloc (arpsize);
memset (arp, 0, arpsize);
@ -87,18 +83,41 @@ int arp_check (interface_t *iface, struct in_addr address)
arp->ar_pro = htons (ETHERTYPE_IP);
arp->ar_hln = iface->hwlen;
arp->ar_pln = sizeof (struct in_addr);
arp->ar_op = htons (ARPOP_REQUEST);
arp->ar_op = htons (op);
memcpy (ar_sha (arp), &iface->hwaddr, arp->ar_hln);
memcpy (ar_tpa (arp), &address, arp->ar_pln);
memcpy (ar_spa (arp), &sip, arp->ar_pln);
if (taddr)
memcpy (ar_tha (arp), taddr, arp->ar_hln);
memcpy (ar_tpa (arp), &tip, arp->ar_pln);
retval = send_packet (iface, ETHERTYPE_ARP,
(unsigned char *) arp, arphdr_len (arp));
free (arp);
return (retval);
}
int arp_claim (interface_t *iface, struct in_addr address)
{
struct arphdr *reply = NULL;
long timeout = 0;
unsigned char *buffer;
int retval = -1;
int nprobes = 0;
int nclaims = 0;
struct in_addr null_address;
if (! iface->arpable) {
logger (LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
return (0);
}
logger (LOG_INFO, "checking %s is available on attached networks",
inet_ntoa (address));
open_socket (iface, true);
send_packet (iface, ETHERTYPE_ARP, (unsigned char *) arp,
arphdr_len (arp));
if (! open_socket (iface, true))
return (0);
timeout = uptime() + TIMEOUT;
memset (&null_address, 0, sizeof (null_address));
buffer = xmalloc (sizeof (char *) * iface->buffer_length);
@ -111,21 +130,40 @@ int arp_check (interface_t *iface, struct in_addr address)
int buflen = sizeof (char *) * iface->buffer_length;
fd_set rset;
int bytes;
int s;
tv.tv_sec = timeout - uptime();
tv.tv_usec = 0;
if (tv.tv_sec < 1)
break; /* Time out */
tv.tv_sec = 0;
tv.tv_usec = timeout;
FD_ZERO (&rset);
FD_SET (iface->fd, &rset);
if (select (FD_SETSIZE, &rset, NULL, NULL, &tv) == 0)
errno = 0;
if ((s = select (FD_SETSIZE, &rset, NULL, NULL, &tv)) == -1) {
if (errno != EINTR)
logger (LOG_ERR, "select: `%s'", strerror (errno));
break;
} else if (s == 0) {
/* Timed out */
if (nprobes < NPROBES) {
nprobes ++;
timeout = PROBE_INTERVAL;
logger (LOG_DEBUG, "sending ARP probe #%d", nprobes);
send_arp (iface, ARPOP_REQUEST, null_address, NULL, address);
} else if (nclaims < NCLAIMS) {
nclaims ++;
timeout = CLAIM_INTERVAL;
logger (LOG_DEBUG, "sending ARP claim #%d", nclaims);
send_arp (iface, ARPOP_REQUEST, address, iface->hwaddr, address);
} else {
/* No replies, so done */
retval = 0;
break;
}
}
if (! FD_ISSET (iface->fd, &rset))
continue;
memset (buffer, 0, buflen);
while (bufpos != 0) {
union {
@ -171,7 +209,6 @@ int arp_check (interface_t *iface, struct in_addr address)
eexit:
close (iface->fd);
iface->fd = -1;
free (arp);
free (buffer);
free (reply);
return (retval);

2
arp.h
View File

@ -25,7 +25,7 @@
#include "interface.h"
int arp_check (interface_t *iface, struct in_addr address);
int arp_claim (interface_t *iface, struct in_addr address);
#endif
#endif

128
client.c
View File

@ -51,6 +51,9 @@
#include "dhcpcd.h"
#include "info.h"
#include "interface.h"
#ifdef ENABLE_IPV4LL
# include "ipv4ll.h"
#endif
#include "logger.h"
#include "signals.h"
#include "socket.h"
@ -132,32 +135,9 @@ static bool daemonise (int *pidfd)
return (true);
}
static unsigned long random_xid (void)
{
static int initialized;
if (! initialized) {
int fd;
unsigned long seed;
fd = open ("/dev/urandom", 0);
if (fd < 0 || read (fd, &seed, sizeof(seed)) < 0) {
logger (LOG_WARNING, "Could not load seed from /dev/urandom: %s",
strerror (errno));
seed = time (0);
}
if (fd >= 0)
close(fd);
srand(seed);
initialized++;
}
return rand();
}
#ifdef ENABLE_INFO
static bool get_old_lease (interface_t *iface, dhcp_t *dhcp, long *timeout)
static bool get_old_lease (const options_t *options, interface_t *iface,
dhcp_t *dhcp, long *timeout)
{
struct timeval tv;
unsigned int offset = 0;
@ -170,6 +150,21 @@ static bool get_old_lease (interface_t *iface, dhcp_t *dhcp, long *timeout)
memset (&dhcp->serveraddress, 0, sizeof (struct in_addr));
memset (dhcp->servername, 0, sizeof (dhcp->servername));
#ifdef ENABLE_ARP
/* Check that no-one is using the address */
if ((options->dolastlease ||
IN_LINKLOCAL (dhcp->address.s_addr)) &&
arp_claim (iface, dhcp->address)) {
memset (&dhcp->address, 0, sizeof (struct in_addr));
memset (&dhcp->netmask, 0, sizeof (struct in_addr));
memset (&dhcp->broadcast, 0, sizeof (struct in_addr));
return (false);
}
/* Ok, lets use this */
if (IN_LINKLOCAL (dhcp->address.s_addr))
return (true);
#endif
/* Ensure that we can still use the lease */
if (gettimeofday (&tv, NULL) == -1) {
@ -191,7 +186,6 @@ static bool get_old_lease (interface_t *iface, dhcp_t *dhcp, long *timeout)
if (timeout)
*timeout = dhcp->renewaltime - offset;
iface->start_uptime = uptime ();
logger (LOG_INFO, "using last known IP address %s", inet_ntoa (dhcp->address));
return (true);
}
#endif
@ -241,7 +235,7 @@ int dhcp_run (const options_t *options, int *pidfd)
(options->doinform || options->dorequest))
{
#ifdef ENABLE_INFO
if (! get_old_lease (iface, dhcp, NULL))
if (! get_old_lease (options, iface, dhcp, NULL))
#endif
{
free (dhcp);
@ -374,7 +368,7 @@ int dhcp_run (const options_t *options, int *pidfd)
{
logger (LOG_INFO, "received SIGHUP, releasing lease");
SOCKET_MODE (SOCKET_OPEN);
xid = random_xid ();
xid = random ();
if ((open_socket (iface, false)) >= 0)
SEND_MESSAGE (DHCP_RELEASE);
SOCKET_MODE (SOCKET_CLOSED);
@ -395,7 +389,11 @@ int dhcp_run (const options_t *options, int *pidfd)
/* timed out */
switch (state) {
case STATE_INIT:
if (iface->previous_address.s_addr != 0 && ! options->doinform) {
if (iface->previous_address.s_addr != 0 &&
#ifdef ENABLE_IPV4LL
! IN_LINKLOCAL (iface->previous_address.s_addr) &&
#endif
! options->doinform) {
logger (LOG_ERR, "lost lease");
xid = 0;
SOCKET_MODE (SOCKET_CLOSED);
@ -404,31 +402,64 @@ int dhcp_run (const options_t *options, int *pidfd)
}
if (xid == 0)
xid = random_xid ();
xid = random ();
else {
SOCKET_MODE (SOCKET_CLOSED);
logger (LOG_ERR, "timed out");
free_dhcp (dhcp);
memset (dhcp, 0, sizeof (dhcp_t));
#ifdef ENABLE_INFO
if (options->dolastlease) {
if (! get_old_lease (iface, dhcp, &timeout))
if (! daemonised ||
configure (options, iface, dhcp, true))
{
retval = EXIT_FAILURE;
goto eexit;
}
if (! get_old_lease (options, iface, dhcp, &timeout)) {
if (options->dolastlease) {
retval = EXIT_FAILURE;
goto eexit;
}
free_dhcp (dhcp);
memset (dhcp, 0, sizeof (dhcp_t));
}
#endif
#ifdef ENABLE_IPV4LL
if (! dhcp->address.s_addr ||
(! IN_LINKLOCAL (dhcp->address.s_addr) &&
! options->dolastlease))
{
logger (LOG_INFO, "probing for an IPV4LL address");
free_dhcp (dhcp);
memset (dhcp, 0, sizeof (dhcp_t));
if (ipv4ll_get_address (iface, dhcp) == -1)
break;
timeout = dhcp->renewaltime;
}
#endif
#if defined (ENABLE_INFO) || defined (ENABLE_IPV4LL)
if (dhcp->address.s_addr)
{
if (configure (options, iface, dhcp, true) < 0 &&
! daemonised)
{
retval = EXIT_FAILURE;
goto eexit;
}
state = STATE_BOUND;
if (! daemonised && options->daemonise) {
if ((daemonise (pidfd)) < 0) {
retval = -1;
retval = EXIT_FAILURE;
goto eexit;
}
daemonised = true;
}
continue;
}
timeout = dhcp->renewaltime;
xid = 0;
break;
}
#endif
if (! daemonised) {
retval = EXIT_FAILURE;
goto eexit;
@ -456,10 +487,19 @@ int dhcp_run (const options_t *options, int *pidfd)
break;
case STATE_BOUND:
case STATE_RENEW_REQUESTED:
#ifdef ENABLE_IPV4LL
if (IN_LINKLOCAL (dhcp->address.s_addr)) {
memset (&dhcp->address, 0, sizeof (struct in_addr));
state = STATE_INIT;
xid = 0;
break;
}
#endif
state = STATE_RENEWING;
xid = random_xid ();
xid = random ();
case STATE_RENEWING:
iface->start_uptime = uptime ();
free_dhcp (dhcp);
logger (LOG_INFO, "renewing lease of %s", inet_ntoa
(dhcp->address));
SOCKET_MODE (SOCKET_OPEN);
@ -472,7 +512,7 @@ int dhcp_run (const options_t *options, int *pidfd)
memset (&dhcp->address, 0, sizeof (struct in_addr));
SOCKET_MODE (SOCKET_OPEN);
if (xid == 0)
xid = random_xid ();
xid = random ();
SEND_MESSAGE (DHCP_REQUEST);
timeout = dhcp->leasetime - dhcp->rebindtime;
state = STATE_REQUESTING;
@ -604,7 +644,7 @@ int dhcp_run (const options_t *options, int *pidfd)
if (options->doarp && iface->previous_address.s_addr !=
dhcp->address.s_addr)
{
if (arp_check (iface, dhcp->address)) {
if (arp_claim (iface, dhcp->address)) {
SOCKET_MODE (SOCKET_OPEN);
SEND_MESSAGE (DHCP_DECLINE);
SOCKET_MODE (SOCKET_CLOSED);

View File

@ -23,10 +23,33 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "common.h"
#include "logger.h"
/* OK, this should be in dhcpcd.c
* It's here to make dhcpcd more readable */
#ifdef __linux__
#include <fcntl.h>
#include <unistd.h>
void srandomdev (void) {
int fd;
unsigned long seed;
fd = open ("/dev/urandom", 0);
if (fd == -1 || read (fd, &seed, sizeof(seed)) == -1) {
logger (LOG_WARNING, "Could not load seed from /dev/urandom: %s",
strerror (errno));
seed = time (0);
}
if (fd >= 0)
close(fd);
srandom (seed);
}
#endif
/* strlcpy is nice, shame glibc does not define it */
#ifdef __GLIBC__
# if ! defined(__UCLIBC__) && ! defined (__dietlibc__)
@ -65,7 +88,6 @@ long uptime (void)
}
#elif __APPLE__
/* Darwin doesn't appear to have an uptime, so try and make one ourselves */
#include <sys/time.h>
long uptime (void)
{
struct timeval tv;
@ -82,7 +104,6 @@ long uptime (void)
return tv.tv_sec - start;
}
#else
#include <time.h>
long uptime (void)
{
struct timespec tp;

View File

@ -32,6 +32,10 @@ size_t strlcpy (char *dst, const char *src, size_t size);
# endif
#endif
#ifdef __linux__
void srandomdev (void);
#endif
long uptime (void);
void *xmalloc (size_t size);
char *xstrdup (const char *str);

View File

@ -33,6 +33,10 @@
/* Define this to enable some compatability with 1.x and 2.x info files */
// #define ENABLE_INFO_COMPAT
/* IPV4LL, aka ZeroConf, aka APIPA, aka RFC 3927.
* Needs ARP. */
#define ENABLE_IPV4LL
/* We will auto create a DUID_LLT file if it doesn't exist.
* You can always create your own DUID file that just contains the
* hex string that represents the DUID.

View File

@ -1,6 +1,6 @@
.\" $Id$
.\"
.TH DHCPCD 8 "12 Jul 2007" "dhcpcd 3.1"
.TH DHCPCD 8 "18 Jul 2007" "dhcpcd 3.1"
.SH NAME
dhcpcd \- DHCP client daemon
@ -9,7 +9,7 @@ dhcpcd \- DHCP client daemon
.in +.5i
.ti -.5i
dhcpcd
\%[\-adknpEGHMNRTY]
\%[\-dknpAEGHMLNRTY]
\%[\-c\ script]
\%[\-h\ hostname]
\%[\-i\ vendorClassID]
@ -26,27 +26,26 @@ dhcpcd
.SH DESCRIPTION
.B dhcpcd
is an implementation of the DHCP client specified in
.B RFC2131.
.B RFC 2131.
It gets the host information (IP address, netmask, broadcast address,
etc.) from a DHCP server and configures the network interface of the
machine on which it is running. It also tries to renew the lease time
according to
.B RFC2131.
.B RFC 2131.
If
.B dhcpcd
fails to get a lease then we attempt Dynamic Configuration of IPv4
Link-Local Addresses unless the
.B \-L
option is given.
.SH OPTIONS
.TP
.BI interface
Specifies the network interface name (eth0, eth1, etc.).
.TP
.BI \-a
Do an
.B ARP
check on the IP address give to us by the DHCP server. We may need to do this
if a client on the same network segment has the same IP address, however we do
not do this by default as most DHCP servers test the IP briefly with an ICMP
Echo request before assigning the IP address.
.TP
.BI \-c \ script
.B dhcpcd
will try to execute
@ -187,6 +186,11 @@ fixed hardware addresses. You can specify more than one user class, but the
total length must be less than 255 characters, -1 character for each user
class.
.TP
.BI \-A
Don't do an
.B ARP
check on the IP address.
.TP
.BI \-E
Will read
.I /var/lib/dhcpcd/dhcpcd-<interface>.info
@ -266,6 +270,10 @@ and store the DUID part in
uses the MAC address of the network interface. If \fB-I\fR is not given
an option then we use the MAC address of the network interface.
.TP
.BI \-L
Prevents dhcpcd from probing for IPV4LL addresses. IPV4LL is otherwise
known as ZeroConf or APIPA and is \fBRFC 3927\fR.
.TP
.BI \-M
Prevents
.B dhcpcd
@ -353,13 +361,16 @@ is attached.
.SH SEE ALSO
.LP
.I Dynamic Host Configuration Protocol,
RFC2132
RFC 2131
.LP
.I DHCP Options and BOOTP Vendor Extensions,
RFC2132
RFC 2132
.LP
.I Dynamic Configuration of IPv4 Link-Local Addresses,
RFC 3927
.LP
.I DHCP FQDN Option specification,
RFC4702
RFC 4702
.SH BUGS
Please report them to http://dhcpcd.berlios.de or http://bugs.gentoo.org.

View File

@ -118,6 +118,7 @@ int main(int argc, char **argv)
{"nogateway", no_argument, NULL, 'G'},
{"sethostname", no_argument, NULL, 'H'},
{"clientid", optional_argument, NULL, 'I'},
{"noipv4ll", no_argument, NULL, 'L'},
{"nomtu", no_argument, NULL, 'M'},
{"nontp", no_argument, NULL, 'N'},
{"nodns", no_argument, NULL, 'R'},
@ -139,7 +140,7 @@ int main(int argc, char **argv)
snprintf (options.classid, CLASS_ID_MAX_LEN, "%s %s", PACKAGE, VERSION);
options.classid_len = strlen (options.classid);
options.doarp = false;
options.doarp = true;
options.dodns = true;
options.domtu = true;
options.donis = true;
@ -147,6 +148,7 @@ int main(int argc, char **argv)
options.dogateway = true;
options.daemonise = true;
options.doinform = false;
options.doipv4ll = true;
options.timeout = DEFAULT_TIMEOUT;
gethostname (options.hostname, sizeof (options.hostname));
@ -156,7 +158,7 @@ int main(int argc, char **argv)
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
while ((opt = getopt_long(argc, argv, "ac:dh:i:kl:m:npr:s:t:u:EF:GHI:MNRTY",
while ((opt = getopt_long(argc, argv, "c:dh:i:kl:m:npr:s:t:u:AEF:GHI:LMNRTY",
longopts, &option_index)) != -1)
{
switch (opt) {
@ -167,14 +169,6 @@ int main(int argc, char **argv)
longopts[option_index].name);
exit (EXIT_FAILURE);
break;
case 'a':
#ifndef ENABLE_ARP
logger (LOG_ERR, "arp support not compiled into dhcpcd");
exit (EXIT_FAILURE);
#endif
options.doarp = true;
break;
case 'c':
options.script = optarg;
break;
@ -283,6 +277,13 @@ int main(int argc, char **argv)
options.userclass_len += (strlen (optarg)) + 1;
}
break;
case 'A':
#ifndef ENABLE_ARP
logger (LOG_ERR, "arp support not compiled into dhcpcd");
exit (EXIT_FAILURE);
#endif
options.doarp = false;
break;
case 'E':
#ifndef ENABLE_INFO
logger (LOG_ERR, "info support not compiled into dhcpcd");
@ -325,6 +326,9 @@ int main(int argc, char **argv)
options.clientid_len = -1;
}
break;
case 'L':
options.doipv4ll = false;
break;
case 'M':
options.domtu = false;
break;
@ -477,6 +481,9 @@ int main(int argc, char **argv)
logger (LOG_INFO, PACKAGE " " VERSION " starting");
}
/* Seed random */
srandomdev ();
if (dhcp_run (&options, &pidfd)) {
if (pidfd > -1)
close (pidfd);

View File

@ -61,6 +61,7 @@ typedef struct options_t {
bool dolastlease;
bool doinform;
bool dorequest;
bool doipv4ll;
struct in_addr request_address;
struct in_addr request_netmask;

63
ipv4ll.c Normal file
View File

@ -0,0 +1,63 @@
/*
* dhcpcd - DHCP client daemon -
* Copyright 2006-2007 Roy Marples <uberlord@gentoo.org>
*
* dhcpcd is an RFC2131 compliant DHCP client daemon.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <stdlib.h>
#include "config.h"
#include "arp.h"
#include "ipv4ll.h"
#ifdef ENABLE_IPV4LL
#ifndef ENABLE_ARP
#error IPV4LL requires ARP
#endif
#define IPV4LL_LEASETIME 10
int ipv4ll_get_address (interface_t *iface, dhcp_t *dhcp) {
struct in_addr addr;
while (1) {
addr.s_addr = htonl (LINKLOCAL_ADDR |
((abs (random ()) % 0xFD00) + 0x0100));
errno = 0;
if (! arp_claim (iface, addr))
break;
/* Our ARP may have been interrupted */
if (errno == EINTR)
return (-1);
}
dhcp->address.s_addr = addr.s_addr;
dhcp->netmask.s_addr = htonl (LINKLOCAL_MASK);
dhcp->broadcast.s_addr = htonl (LINKLOCAL_BRDC);
/* Finally configure some DHCP like lease times */
dhcp->leasetime = IPV4LL_LEASETIME;
dhcp->renewaltime = (dhcp->leasetime * 0.5);
dhcp->rebindtime = (dhcp->leasetime * 0.875);
return (0);
}
#endif

37
ipv4ll.h Normal file
View File

@ -0,0 +1,37 @@
/*
* dhcpcd - DHCP client daemon -
* Copyright 2005 - 2007 Roy Marples <uberlord@gentoo.org>
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef IPV4LL_H
#define IPV4LL_H
#ifdef ENABLE_IPV4LL
#include "dhcp.h"
#include "interface.h"
#define LINKLOCAL_ADDR 0xa9fe0000
#define LINKLOCAL_MASK 0xffff0000
#define LINKLOCAL_BRDC 0xa9feffff
#define IN_LINKLOCAL(addr) ((ntohl (addr) & IN_CLASSB_NET) == LINKLOCAL_ADDR)
int ipv4ll_get_address (interface_t *iface, dhcp_t *dhcp);
#endif
#endif