It fix DHCP ACK lost issue when doing DHCP renewal.
When doing DHCP renew, 2 sockets are opened. One is for
listening DHCP ACK, the other is for transmitting DHCP request
which is closed immediately after transmitting is done. But in
some cases, the socket is closed after the DHCP ACK is received.
The kernel will route the packet to the transmitting socket
because it has a better match result(dst ip/port etc). And the
packet was dropped when the socket was closed.
---
gdhcp/client.c | 6 ++++--
gdhcp/common.c | 57 +++++++++++++++++++++++++++++++++++----------------------
gdhcp/common.h | 2 +-
3 files changed, 40 insertions(+), 25 deletions(-)
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 3bf8cb2..ad587b1 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -502,7 +502,8 @@ static int send_request(GDHCPClient *dhcp_client)
if (dhcp_client->state == RENEWING)
return dhcp_send_kernel_packet(&packet,
dhcp_client->requested_ip, CLIENT_PORT,
- dhcp_client->server_ip, SERVER_PORT);
+ dhcp_client->server_ip, SERVER_PORT,
+ dhcp_client->listener_sockfd);
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
INADDR_BROADCAST, SERVER_PORT,
@@ -526,7 +527,8 @@ static int send_release(GDHCPClient *dhcp_client,
dhcp_add_option_uint32(&packet, DHCP_SERVER_ID, server);
return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
- server, SERVER_PORT);
+ server, SERVER_PORT,
+ dhcp_client->listener_sockfd);
}
static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
diff --git a/gdhcp/common.c b/gdhcp/common.c
index f3d4677..c841a0a 100644
--- a/gdhcp/common.c
+++ b/gdhcp/common.c
@@ -626,46 +626,59 @@ int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
- uint32_t dest_ip, int dest_port)
+ uint32_t dest_ip, int dest_port, int openedfd)
{
struct sockaddr_in client;
- int fd, n, opt = 1;
+ int n, fd, ret, opt = 1;
enum {
DHCP_SIZE = sizeof(struct dhcp_packet) -
EXTEND_FOR_BUGGY_SERVERS,
};
- fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
- if (fd < 0)
- return -errno;
-
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-
- memset(&client, 0, sizeof(client));
- client.sin_family = AF_INET;
- client.sin_port = htons(source_port);
- client.sin_addr.s_addr = htonl(source_ip);
- if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
- close(fd);
- return -errno;
+ if (openedfd < 0) {
+ /* no socket opened, open a new socket to tx the packet and close it */
+ fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if (fd < 0)
+ return -errno;
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ memset(&client, 0, sizeof(client));
+ client.sin_family = AF_INET;
+ client.sin_port = htons(source_port);
+ client.sin_addr.s_addr = htonl(source_ip);
+ if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
+ ret = -errno;
+ close(fd);
+ return ret;
+ }
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(dest_port);
client.sin_addr.s_addr = htonl(dest_ip);
- if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
- close(fd);
- return -errno;
- }
- n = write(fd, dhcp_pkt, DHCP_SIZE);
+ if (openedfd < 0) {
+ if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
+ ret = -errno;
+ close(fd);
+ return ret;
+ }
- close(fd);
+ n = write(fd, dhcp_pkt, DHCP_SIZE);
+ ret = -errno;
+ close(fd);
+ } else {
+ /* Using existed socket to transmit the packet */
+ n = sendto(openedfd, dhcp_pkt, DHCP_SIZE, MSG_DONTWAIT,
+ (struct sockaddr *) &client, sizeof(client));
+ ret = -errno;
+ }
if (n < 0)
- return -errno;
+ return ret;
return n;
}
diff --git a/gdhcp/common.h b/gdhcp/common.h
index 75abc18..b92d214 100644
--- a/gdhcp/common.h
+++ b/gdhcp/common.h
@@ -209,7 +209,7 @@ int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len);
int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
- uint32_t dest_ip, int dest_port);
+ uint32_t dest_ip, int dest_port, int fd);
int dhcp_l3_socket(int port, const char *interface, int family);
int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd);
int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
--
2.7.0.rc3.207.g0ac5344