#include <config.h>
#endif
+#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
} ClientState;
struct _GDHCPClient {
- gint ref_count;
+ int ref_count;
GDHCPType type;
ClientState state;
int ifindex;
gpointer address_conflict_data;
GDHCPDebugFunc debug_func;
gpointer debug_data;
+ char *last_address;
};
static inline void debug(GDHCPClient *client, const char *format, ...)
static int send_select(GDHCPClient *dhcp_client)
{
struct dhcp_packet packet;
- struct in_addr addr;
debug(dhcp_client, "sending DHCP select request");
add_send_options(dhcp_client, &packet);
- addr.s_addr = dhcp_client->requested_ip;
-
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
INADDR_BROADCAST, SERVER_PORT,
MAC_BCAST_ADDR, dhcp_client->ifindex);
struct ifreq ifr;
int sk, err;
- sk = socket(PF_INET, SOCK_DGRAM, 0);
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (sk < 0) {
perror("Open socket error");
return;
.filter = (struct sock_filter *) filter_instr,
};
- fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
if (fd < 0)
return fd;
dhcp_client->requested_ip = 0;
switch_listening_mode(dhcp_client, L2);
- g_dhcp_client_start(dhcp_client);
+ g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
}
static gboolean start_rebound_timeout(gpointer user_data)
else {
send_renew(dhcp_client);
+ if (dhcp_client->timeout > 0)
+ g_source_remove(dhcp_client->timeout);
+
dhcp_client->timeout =
g_timeout_add_seconds_full(G_PRIORITY_HIGH,
dhcp_client->lease_seconds >> 1,
dhcp_client->state = BOUND;
+ if (dhcp_client->timeout > 0)
+ g_source_remove(dhcp_client->timeout);
+
dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
dhcp_client->lease_seconds >> 1,
start_renew_timeout, dhcp_client,
dhcp_client->retry_times++;
- g_dhcp_client_start(dhcp_client);
+ /*
+ * We do not send the REQUESTED IP option if we are retrying because
+ * if the server is non-authoritative it will ignore the request if the
+ * option is present.
+ */
+ g_dhcp_client_start(dhcp_client, NULL);
return FALSE;
}
return FALSE;
}
-int g_dhcp_client_start(GDHCPClient *dhcp_client)
+int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
{
int re;
+ uint32_t addr;
if (dhcp_client->retry_times == DISCOVER_RETRIES) {
ipv4ll_start(dhcp_client);
dhcp_client->xid = rand();
}
- send_discover(dhcp_client, 0);
+ if (last_address == NULL) {
+ addr = 0;
+ } else {
+ addr = inet_addr(last_address);
+ if (addr == 0xFFFFFFFF) {
+ addr = 0;
+ } else {
+ g_free(dhcp_client->last_address);
+ dhcp_client->last_address = g_strdup(last_address);
+ }
+ }
+ send_discover(dhcp_client, addr);
dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
DISCOVER_TIMEOUT,
return g_strdup(dhcp_client->assigned_ip);
}
+char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
+{
+ GList *option = NULL;
+
+ switch (dhcp_client->state) {
+ case IPV4LL_DEFEND:
+ case IPV4LL_MONITOR:
+ return g_strdup("255.255.0.0");
+ case BOUND:
+ case RENEWING:
+ case REBINDING:
+ option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
+ if (option != NULL)
+ return g_strdup(option->data);
+ case INIT_SELECTING:
+ case REQUESTING:
+ case RELEASED:
+ case IPV4LL_PROBE:
+ case IPV4LL_ANNOUNCE:
+ break;
+ }
+ return NULL;
+}
+
GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
unsigned char option_code)
{
if (dhcp_client == NULL)
return NULL;
- g_atomic_int_inc(&dhcp_client->ref_count);
+ __sync_fetch_and_add(&dhcp_client->ref_count, 1);
return dhcp_client;
}
if (dhcp_client == NULL)
return;
- if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
+ if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
return;
g_dhcp_client_stop(dhcp_client);
g_free(dhcp_client->interface);
g_free(dhcp_client->assigned_ip);
+ g_free(dhcp_client->last_address);
g_list_free(dhcp_client->request_list);
g_list_free(dhcp_client->require_list);