+Enable DHCP_INFORM support via the -S option (requires -s ipaddr too)
Use the DUID stored to create an RFC 4361 conformant client identifier
if none is specified instead of just using the MAC address.
Create a DUID-LLT according to RFC 3315 and store it in peristent file.
/* We need this for our maximum timeout as FreeBSD's select cannot handle
any higher than this. Is there a better way of working this out? */
-#define SELECT_MAX 100000000
+#define SELECT_MAX 100000000
/* This is out mini timeout.
Basically we resend the last request every TIMEOUT_MINI seconds. */
-#define TIMEOUT_MINI 3
+#define TIMEOUT_MINI 3
/* Except for an infinite timeout. We keep adding TIMEOUT_MINI to
ourself until TIMEOUT_MINI_INF is reached. */
-#define TIMEOUT_MINI_INF 60
+#define TIMEOUT_MINI_INF 60
-#define STATE_INIT 0
-#define STATE_REQUESTING 1
-#define STATE_BOUND 2
-#define STATE_RENEWING 3
-#define STATE_REBINDING 4
-#define STATE_REBOOT 5
-#define STATE_RENEW_REQUESTED 6
-#define STATE_RELEASED 7
+#define STATE_INIT 0
+#define STATE_REQUESTING 1
+#define STATE_BOUND 2
+#define STATE_RENEWING 3
+#define STATE_REBINDING 4
+#define STATE_REBOOT 5
+#define STATE_RENEW_REQUESTED 6
+#define STATE_RELEASED 7
-#define SOCKET_CLOSED 0
-#define SOCKET_OPEN 1
+#define SOCKET_CLOSED 0
+#define SOCKET_OPEN 1
#define SOCKET_MODE(_mode) { \
if (iface->fd >= 0) close (iface->fd); \
unsigned char *buffer = NULL;
int buffer_len = 0;
int buffer_pos = 0;
+ struct in_addr netmask;
+ struct in_addr broadcast;
if (! options || (iface = (read_interface (options->interface,
options->metric))) == NULL)
return -1;
+ dhcp = xmalloc (sizeof (dhcp_t));
+ memset (dhcp, 0, sizeof (dhcp_t));
+
+ memset (&netmask, 0, sizeof (struct in_addr));
+ memset (&broadcast, 0, sizeof (struct in_addr));
+
/* Remove all existing addresses.
After all, we ARE a DHCP client whose job it is to configure the
interface. We only do this on start, so persistent addresses can be added
afterwards by the user if needed. */
- flush_addresses (iface->name);
-
- dhcp = xmalloc (sizeof (dhcp_t));
- memset (dhcp, 0, sizeof (dhcp_t));
-
- if (options->requestaddress.s_addr != 0)
+ if (! options->doinform)
+ flush_addresses (iface->name);
+ else {
dhcp->address.s_addr = options->requestaddress.s_addr;
+ /* The inform address HAS to be configured for it to work with most
+ * DHCP servers */
+ if (options->doinform && has_address (iface->name, dhcp->address) < 1) {
+ netmask.s_addr = get_netmask (dhcp->address.s_addr);
+ broadcast.s_addr = dhcp->address.s_addr | ~netmask.s_addr;
+ add_address (iface->name, dhcp->address, netmask, broadcast);
+ iface->previous_address = dhcp->address;
+ iface->previous_netmask = netmask;
+ }
+ }
+
signal_setup ();
while (1) {
/* timed out */
switch (state) {
case STATE_INIT:
- if (iface->previous_address.s_addr != 0) {
+ if (iface->previous_address.s_addr != 0 && ! options->doinform) {
logger (LOG_ERR, "lost lease");
xid = 0;
SOCKET_MODE (SOCKET_CLOSED);
if (dhcp->address.s_addr == 0) {
logger (LOG_INFO, "broadcasting for a lease");
SEND_MESSAGE (DHCP_DISCOVER);
+ } else if (options->doinform) {
+ logger (LOG_INFO, "broadcasting inform for %s",
+ inet_ntoa (dhcp->address));
+ SEND_MESSAGE (DHCP_INFORM);
+ state = STATE_REQUESTING;
} else {
logger (LOG_INFO, "broadcasting for a lease of %s",
inet_ntoa (dhcp->address));
state = STATE_REQUESTING;
break;
case STATE_REQUESTING:
- if (iface->previous_address.s_addr != 0)
+ if (iface->previous_address.s_addr != 0 && ! options->doinform)
logger (LOG_ERR, "lost lease");
else
logger (LOG_ERR, "timed out");
SOCKET_MODE (SOCKET_CLOSED);
timeout = 0;
xid = 0;
- DROP_CONFIG;
+ if (! options->doinform)
+ DROP_CONFIG;
break;
case STATE_RELEASED:
}
#endif
- if (dhcp->leasetime == (unsigned) -1) {
+ if (options->doinform) {
+ dhcp->address = options->requestaddress;
+ logger (LOG_INFO, "received approval for %s",
+ inet_ntoa (dhcp->address));
+ timeout = options->leasetime;
+ if (timeout == 0)
+ timeout = DEFAULT_LEASETIME;
+ state = STATE_INIT;
+ } else if (dhcp->leasetime == (unsigned) -1) {
dhcp->renewaltime = dhcp->rebindtime = dhcp->leasetime;
timeout = 1; /* So we select on infinity */
logger (LOG_INFO, "leased %s for infinity",
inet_ntoa (dhcp->address));
+ state = STATE_BOUND;
} else {
if (! dhcp->leasetime) {
dhcp->leasetime = DEFAULT_LEASETIME;
dhcp->rebindtime);
timeout = dhcp->renewaltime;
+ state = STATE_BOUND;
}
- state = STATE_BOUND;
xid = 0;
if (configure (options, iface, dhcp) < 0 && ! daemonised) {
/* Only reset things if we had set them before */
if (iface->previous_address.s_addr != 0) {
- del_address (iface->name, iface->previous_address,
- iface->previous_netmask);
- memset (&iface->previous_address, 0, sizeof (struct in_addr));
- memset (&iface->previous_netmask, 0, sizeof (struct in_addr));
+ if (! options->keep_address) {
+ del_address (iface->name, iface->previous_address,
+ iface->previous_netmask);
+ memset (&iface->previous_address, 0, sizeof (struct in_addr));
+ memset (&iface->previous_netmask, 0, sizeof (struct in_addr));
+ }
restore_resolv (iface->name);
}
}
- if (add_address (iface->name, dhcp->address, dhcp->netmask,
- dhcp->broadcast) < 0 && errno != EEXIST)
- return -1;
+ if (! options->doinform || ! has_address (iface->name, dhcp->address))
+ if (add_address (iface->name, dhcp->address, dhcp->netmask,
+ dhcp->broadcast) < 0 && errno != EEXIST)
+ return -1;
/* Now delete the old address if different */
- if (iface->previous_address.s_addr != dhcp->address.s_addr
- && iface->previous_address.s_addr != 0)
+ if (iface->previous_address.s_addr != dhcp->address.s_addr &&
+ iface->previous_address.s_addr != 0 &&
+ ! options->keep_address)
del_address (iface->name, iface->previous_address, iface->previous_netmask);
#ifdef __linux__
{
message.ciaddr = iface->previous_address.s_addr;
from.s_addr = iface->previous_address.s_addr;
+
+ /* Just incase we haven't actually configured the address yet */
+ if (type == DHCP_INFORM && iface->previous_address.s_addr == 0)
+ message.ciaddr = dhcp->address.s_addr;
}
message.op = DHCP_BOOTREQUEST;
p += 2;
}
+ if (type != DHCP_INFORM) {
#define PUTADDR(_type, _val) \
{ \
*p++ = _type; \
(iface->previous_address.s_addr == 0 || type == DHCP_RELEASE))
PUTADDR (DHCP_SERVERIDENTIFIER, dhcp->serveraddress);
#undef PUTADDR
+ }
if (type == DHCP_REQUEST || type == DHCP_DISCOVER) {
if (options->leasetime != 0) {
if (type == DHCP_DISCOVER)
*p++ = DHCP_DNSSERVER;
else {
- *p++ = DHCP_RENEWALTIME;
- *p++ = DHCP_REBINDTIME;
+ if (type != DHCP_INFORM) {
+ *p++ = DHCP_RENEWALTIME;
+ *p++ = DHCP_REBINDTIME;
+ }
*p++ = DHCP_NETMASK;
*p++ = DHCP_BROADCAST;
*p++ = DHCP_CSR;
sizeof (struct udphdr));
}
-static unsigned long getnetmask (unsigned long ip_in)
-{
- unsigned long t, p = ntohl (ip_in);
-
- if (IN_CLASSA (p))
- t = ~IN_CLASSA_NET;
- else {
- if (IN_CLASSB (p))
- t = ~IN_CLASSB_NET;
- else {
- if (IN_CLASSC (p))
- t = ~IN_CLASSC_NET;
- else
- t = 0;
- }
- }
- while (t & p) t >>= 1;
- return htonl (~t);
-}
-
/* Decode an RFC3397 DNS search order option into a space
seperated string. Returns length of string (including
terminating zero) or zero on error. out may be NULL
memcpy (&static_routesp->destination.s_addr, p + i, 4);
memcpy (&static_routesp->gateway.s_addr, p + i + 4, 4);
static_routesp->netmask.s_addr =
- getnetmask (static_routesp->destination.s_addr);
+ get_netmask (static_routesp->destination.s_addr);
}
break;
eexit:
/* Fill in any missing fields */
if (! dhcp->netmask.s_addr)
- dhcp->netmask.s_addr = getnetmask (dhcp->address.s_addr);
+ dhcp->netmask.s_addr = get_netmask (dhcp->address.s_addr);
if (! dhcp->broadcast.s_addr)
dhcp->broadcast.s_addr = dhcp->address.s_addr | ~dhcp->netmask.s_addr;
.\" $Id$
.\"
-.TH DHCPCD 8 "18 April 2007" "dhcpcd 3.0"
+.TH DHCPCD 8 "11 May 2007" "dhcpcd 3.1"
.SH NAME
dhcpcd \- DHCP client daemon
.in +.5i
.ti -.5i
dhcpcd
-\%[\-adknpGHMNRTY]
+\%[\-adknpGHMNRSTY]
\%[\-c\ script]
\%[\-h\ hostname]
\%[\-i\ vendorClassID]
The ipaddr parameter must be in the form xxx.xxx.xxx.xxx.
This effectively doubles the timeout period, as if we fail to get
this IP address then we enter the INIT state and try to get any
-IP address.
+IP address. To send a DHCP_INFORM message use the -S option.
.TP
.BI \-t \ timeout
Specifies (in seconds ) for how long
Specifies the client identifier string. If not specified then
.B dhcpcd
will attempt to create a client identifier according to \fBRFC 4361\fR
-and store the DUID part in /var/lib/dhcpcd/dhcpcd.duid, otherwise
+and store the DUID part in
+.I /var/lib/dhcpcd/dhcpcd.duid\fR, otherwise
.B dhcpcd
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.
.I /etc/resolv.conf
or using resolvconf.
.TP
+.BI \-S
+Sends a DHCP_INFORM message instead of a DHCP_REQUEST message when using the
+.BI \-s
+option. When we DHCP_INFORM, we don't request or respect any lease times.
+However, we do re-inform by the lease time specified by
+.BI \-l
+if given.
+.TP
.BI \-T
Will read
.I /var/lib/dhcpcd/dhcpcd-<interface>.info
static void usage ()
{
- printf ("usage: "PACKAGE" [-adknpGHLMNRY] [-c script] [-h hostame] [-i classID]\n"
+ printf ("usage: "PACKAGE" [-adknpEGHMNRSY] [-c script] [-h hostame] [-i classID]\n"
" [-l leasetime] [-m metric] [-s ipaddress] [-t timeout]\n"
- " [-u userclass] [-F [none | ptr | both]] [-I clientID]\n");
+ " [-u userclass] [-F [none | ptr | both]] [-I [clientID]]\n");
}
int main(int argc, char **argv)
int i;
const struct option longopts[] = {
- {"arp", no_argument, NULL, 'a'},
- {"script", required_argument, NULL, 'c'},
- {"debug", no_argument, NULL, 'd'},
- {"hostname", required_argument, NULL, 'h'},
- {"classid", required_argument, NULL, 'i'},
- {"release", no_argument, NULL, 'k'},
- {"leasetime", required_argument, NULL, 'l'},
- {"metric", required_argument, NULL, 'm'},
- {"renew", no_argument, NULL, 'n'},
- {"persistent", no_argument, NULL, 'p'},
- {"request", required_argument, NULL, 's'},
- {"timeout", required_argument, NULL, 't'},
- {"userclass", required_argument, NULL, 'u'},
- {"lastlease", no_argument, NULL, 'E'},
+ {"arp", no_argument, NULL, 'a'},
+ {"script", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"hostname", required_argument, NULL, 'h'},
+ {"classid", required_argument, NULL, 'i'},
+ {"release", no_argument, NULL, 'k'},
+ {"leasetime", required_argument, NULL, 'l'},
+ {"metric", required_argument, NULL, 'm'},
+ {"renew", no_argument, NULL, 'n'},
+ {"persistent", no_argument, NULL, 'p'},
+ {"request", required_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 't'},
+ {"userclass", required_argument, NULL, 'u'},
+ {"lastlease", no_argument, NULL, 'E'},
{"fqdn", optional_argument, NULL, 'F'},
- {"nogateway", no_argument, NULL, 'G'},
- {"sethostname", no_argument, NULL, 'H'},
- {"clientid", optional_argument, NULL, 'I'},
- {"nomtu", no_argument, NULL, 'M'},
- {"nontp", no_argument, NULL, 'N'},
- {"nodns", no_argument, NULL, 'R'},
+ {"nogateway", no_argument, NULL, 'G'},
+ {"sethostname", no_argument, NULL, 'H'},
+ {"clientid", optional_argument, NULL, 'I'},
+ {"nomtu", no_argument, NULL, 'M'},
+ {"nontp", no_argument, NULL, 'N'},
+ {"nodns", no_argument, NULL, 'R'},
+ {"inform", no_argument, NULL, 'S'},
{"nonis", no_argument, NULL, 'Y'},
- {"help", no_argument, &dohelp, 1},
- {"version", no_argument, &doversion, 1},
- {NULL, 0, NULL, 0}
+ {"help", no_argument, &dohelp, 1},
+ {"version", no_argument, &doversion, 1},
+ {NULL, 0, NULL, 0}
};
/* Close any un-needed fd's */
options.dontp = true;
options.dogateway = true;
options.daemonise = true;
+ options.doinform = false;
options.timeout = DEFAULT_TIMEOUT;
- while ((ch = getopt_long(argc, argv, "ac:dh:i:kl:m:nps:t:u:EF::GHI::MNRY", longopts,
+ while ((ch = getopt_long(argc, argv, "ac:dh:i:kl:m:nps::t:u:EF::GHI::MNRSY", longopts,
&option_index)) != -1)
{
switch (ch) {
break;
case 'h':
if (strlen (optarg) > sizeof (options.hostname)) {
- logger (LOG_ERR, "`%s' too long for HostName string, max is %d",
+ logger (LOG_ERR, "`%s' too long for HostName string, max is %lu",
optarg, sizeof (options.hostname));
exit (EXIT_FAILURE);
} else
options.persistent = true;
break;
case 's':
- if (! inet_aton (optarg, &options.requestaddress)) {
+ if (! inet_aton (optarg, &options.requestaddress)) {
logger (LOG_ERR, "`%s' is not a valid IP address", optarg);
exit (EXIT_FAILURE);
}
case 'R':
options.dodns = false;
break;
+ case 'S':
+ options.doinform = true;
+ break;
case 'Y':
options.donis = false;
break;
options.fqdn = FQDN_BOTH;
} else
options.fqdn = FQDN_DISABLE;
-
+
+ if (options.doinform && options.requestaddress.s_addr == 0) {
+ if ((options.requestaddress.s_addr = get_address (options.interface)) == 0) {
+ logger (LOG_ERR, "no existing address to inform");
+ exit (EXIT_FAILURE);
+ }
+ options.keep_address = true;
+ }
+
if (geteuid ()) {
logger (LOG_ERR, "you need to be root to run "PACKAGE);
exit (EXIT_FAILURE);
bool donis;
bool dontp;
bool dolastlease;
+ bool doinform;
int signal;
bool persistent;
+ bool keep_address;
bool daemonise;
char *script;
fprintf (f, "DHCPSID='%s'\n", inet_ntoa (dhcp->serveraddress));
fprintf (f, "DHCPSNAME='%s'\n", cleanmetas (dhcp->servername));
- fprintf (f, "LEASEDFROM='%u'\n", dhcp->leasedfrom);
- fprintf (f, "LEASETIME='%u'\n", dhcp->leasetime);
- fprintf (f, "RENEWALTIME='%u'\n", dhcp->renewaltime);
- fprintf (f, "REBINDTIME='%u'\n", dhcp->rebindtime);
+ if (! options->doinform) {
+ fprintf (f, "LEASEDFROM='%u'\n", dhcp->leasedfrom);
+ fprintf (f, "LEASETIME='%u'\n", dhcp->leasetime);
+ fprintf (f, "RENEWALTIME='%u'\n", dhcp->renewaltime);
+ fprintf (f, "REBINDTIME='%u'\n", dhcp->rebindtime);
+ }
fprintf (f, "INTERFACE='%s'\n", iface->name);
fprintf (f, "CLASSID='%s'\n", cleanmetas (options->classid));
if (options->clientid_len > 0)
return (cidr);
}
+unsigned long get_netmask (unsigned long addr)
+{
+ unsigned long dst;
+
+ if (addr == 0)
+ return (0);
+
+ dst = htonl (addr);
+ if (IN_CLASSA (dst))
+ return (ntohl (IN_CLASSA_NET));
+ if (IN_CLASSB (dst))
+ return (ntohl (IN_CLASSB_NET));
+ if (IN_CLASSC (dst))
+ return (ntohl (IN_CLASSC_NET));
+
+ return (0);
+}
+
char *hwaddr_ntoa (const unsigned char *hwaddr, int hwlen)
{
static char buffer[128];
}
#ifdef HAVE_IFADDRS_H
-int flush_addresses (const char *ifname)
+static int _do_addresses (const char *ifname, struct in_addr *addr, bool flush, bool get)
{
struct ifaddrs *ifap;
struct ifaddrs *p;
us_a.sa = p->ifa_addr;
us_m.sa = p->ifa_netmask;
- if (us_a.sin->sin_family == AF_INET)
- if (del_address (ifname, us_a.sin->sin_addr, us_m.sin->sin_addr) < 0)
- retval = -1;
+ if (us_a.sin->sin_family == AF_INET) {
+ if (flush) {
+ if (del_address (ifname, us_a.sin->sin_addr, us_m.sin->sin_addr)
+ < 0)
+ retval = -1;
+ } else if (get) {
+ addr->s_addr = us_a.sin->sin_addr.s_addr;
+ retval = 1;
+ break;
+ } else {
+ if (us_a.sin->sin_addr.s_addr == addr->s_addr) {
+ retval = 1;
+ break;
+ }
+ }
+ }
}
freeifaddrs (ifap);
return retval;
}
+
#else
-int flush_addresses (const char *ifname)
+static int _do_addresses (const char *ifname, struct in_addr address, bool flush)
{
int s;
struct ifconf ifc;
if (ifr->ifr_addr.sa_family == AF_INET
&& strcmp (ifname, ifr->ifr_name) == 0)
- if (del_address (ifname, addr->sin_addr, netm->sin_addr) < 0)
- retval = -1;
+ {
+ if (flush) {
+ if (del_address (ifname, addr->sin_addr, netm->sin_addr) < 0)
+ retval = -1;
+ } else if (addr->sin_addr.s_addr == address.s_addr) {
+ retval = 1;
+ break;
+ }
+ }
ifr++;
}
return retval;
}
#endif
+
+int flush_addresses (const char *ifname)
+{
+ struct in_addr address;
+ return (_do_addresses (ifname, &address, true, false));
+}
+
+unsigned long get_address (const char *ifname)
+{
+ struct in_addr address;
+ if (_do_addresses (ifname, &address, false, true) > 0)
+ return (address.s_addr);
+ return (0);
+}
+
+int has_address (const char *ifname, struct in_addr address)
+{
+ return (_do_addresses (ifname, &address, false, false));
+}
+
void free_address (address_t *addresses);
void free_route (route_t *routes);
+unsigned long get_netmask (unsigned long addr);
+char *hwaddr_ntoa (const unsigned char *hwaddr, int hwlen);
+
interface_t *read_interface (const char *ifname, int metric);
int get_mtu (const char *ifname);
int set_mtu (const char *ifname, short int mtu);
struct in_addr netmask, struct in_addr broadcast);
int del_address (const char *ifname, struct in_addr address,
struct in_addr netmask);
+
int flush_addresses (const char *ifname);
+unsigned long get_address (const char *ifname);
+int has_address (const char *ifname, struct in_addr address);
int add_route (const char *ifname, struct in_addr destination,
struct in_addr netmask, struct in_addr gateway, int metric);
struct in_addr netmask, struct in_addr gateway, int metric);
int inet_ntocidr (struct in_addr address);
-char *hwaddr_ntoa (const unsigned char *hwaddr, int hwlen);
#endif