Some routers/AP handle the DHCP broadcast flag incorrectly.
This means that some AP discard the DHCP packet if broadcast
flag is present and some discard it if broadcast flag is missing.
The workaround is to first send DISCOVER packet in INIT state without
broadcast flag. If there is a timeout we send the second packet with
broadcast flag set. In a case of second timeout the next DISCOVER will
not set broadcast flag etc.
In REBOOTING state the REQUEST packet will not set the broadcast flag.
If we do not get a reply, we switch to INIT state anyway which will
set the broadcast flag.
---
Hi,
this is v2 of the DHCP broadcast flag handling. This version will by
default start with unicast flag and only if there is no reply we
try with broadcast flag.
Cheers,
Jukka
gdhcp/client.c | 52 +++++++++++++++++++++++++++++++++++++++++++++-------
gdhcp/common.c | 8 +++++---
gdhcp/common.h | 3 ++-
gdhcp/server.c | 4 ++--
4 files changed, 54 insertions(+), 13 deletions(-)
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 16fe080..66c3a90 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -155,6 +155,7 @@ struct _GDHCPClient {
uint32_t expire;
bool retransmit;
struct timeval start_time;
+ bool request_bcast;
};
static inline void debug(GDHCPClient *client, const char *format, ...)
@@ -455,15 +456,26 @@ static int send_discover(GDHCPClient *dhcp_client,
uint32_t requested)
add_send_options(dhcp_client, &packet);
+ /*
+ * If we do not get a reply to DISCOVER packet, then we try with
+ * broadcast flag set. So first packet is sent without broadcast flag,
+ * first retry is with broadcast flag, second retry is without it etc.
+ * Reason is various buggy routers/AP that either eat the other or vice
+ * versa. In the receiving side we then find out what kind of packet
+ * the server can send.
+ */
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
- INADDR_BROADCAST, SERVER_PORT,
- MAC_BCAST_ADDR, dhcp_client->ifindex);
+ INADDR_BROADCAST, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex,
+ dhcp_client->retry_times % 2);
}
static int send_request(GDHCPClient *dhcp_client)
{
struct dhcp_packet packet;
- debug(dhcp_client, "sending DHCP request");
+
+ debug(dhcp_client, "sending DHCP request (state %d)",
+ dhcp_client->state);
init_packet(dhcp_client, &packet, DHCPREQUEST);
@@ -493,8 +505,9 @@ static int send_request(GDHCPClient *dhcp_client)
dhcp_client->server_ip, SERVER_PORT);
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
- INADDR_BROADCAST, SERVER_PORT,
- MAC_BCAST_ADDR, dhcp_client->ifindex);
+ INADDR_BROADCAST, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex,
+ dhcp_client->request_bcast);
}
static int send_release(GDHCPClient *dhcp_client,
@@ -1186,6 +1199,7 @@ GDHCPClient *g_dhcp_client_new(GDHCPType type,
dhcp_client->duid_len = 0;
dhcp_client->last_request = time(NULL);
dhcp_client->expire = 0;
+ dhcp_client->request_bcast = false;
*error = G_DHCP_CLIENT_ERROR_NONE;
@@ -1293,7 +1307,8 @@ static bool sanity_check(struct ip_udp_dhcp_packet
*packet, int bytes)
return true;
}
-static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
+static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd,
+ struct sockaddr_in *dst_addr)
{
int bytes;
struct ip_udp_dhcp_packet packet;
@@ -1338,6 +1353,8 @@ static int dhcp_recv_l2_packet(struct dhcp_packet
*dhcp_pkt, int fd)
if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
return -1;
+ dst_addr->sin_addr.s_addr = packet.ip.daddr;
+
return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
}
@@ -2231,6 +2248,7 @@ static gboolean listener_event(GIOChannel *channel,
GIOCondition condition,
gpointer user_data)
{
GDHCPClient *dhcp_client = user_data;
+ struct sockaddr_in dst_addr = { 0 };
struct dhcp_packet packet;
struct dhcpv6_packet *packet6 = NULL;
uint8_t *message_type = NULL, *client_id = NULL, *option,
@@ -2257,7 +2275,8 @@ static gboolean listener_event(GIOChannel *channel,
GIOCondition condition,
if (dhcp_client->listen_mode == L2) {
re = dhcp_recv_l2_packet(&packet,
- dhcp_client->listener_sockfd);
+ dhcp_client->listener_sockfd,
+ &dst_addr);
xid = packet.xid;
} else if (dhcp_client->listen_mode == L3) {
if (dhcp_client->type == G_DHCP_IPV6) {
@@ -2347,10 +2366,28 @@ static gboolean listener_event(GIOChannel *channel,
GIOCondition condition,
dhcp_client->state = REQUESTING;
+ if (dst_addr.sin_addr.s_addr == INADDR_BROADCAST)
+ dhcp_client->request_bcast = true;
+ else
+ dhcp_client->request_bcast = false;
+
+ debug(dhcp_client, "init ip %s -> %sadding broadcast flag",
+ inet_ntoa(dst_addr.sin_addr),
+ dhcp_client->request_bcast ? "" : "not ");
+
start_request(dhcp_client);
return TRUE;
case REBOOTING:
+ if (dst_addr.sin_addr.s_addr == INADDR_BROADCAST)
+ dhcp_client->request_bcast = true;
+ else
+ dhcp_client->request_bcast = false;
+
+ debug(dhcp_client, "ip %s -> %sadding broadcast flag",
+ inet_ntoa(dst_addr.sin_addr),
+ dhcp_client->request_bcast ? "" : "not ");
+ /* fall through */
case REQUESTING:
case RENEWING:
case REBINDING:
@@ -2841,6 +2878,7 @@ void g_dhcp_client_stop(GDHCPClient *dhcp_client)
dhcp_client->requested_ip = 0;
dhcp_client->state = RELEASED;
dhcp_client->lease_seconds = 0;
+ dhcp_client->request_bcast = false;
}
GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
diff --git a/gdhcp/common.c b/gdhcp/common.c
index e111150..45278a8 100644
--- a/gdhcp/common.c
+++ b/gdhcp/common.c
@@ -511,8 +511,9 @@ int dhcpv6_send_packet(int index, struct dhcpv6_packet
*dhcp_pkt, int len)
}
int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
- uint32_t source_ip, int source_port, uint32_t dest_ip,
- int dest_port, const uint8_t *dest_arp, int ifindex)
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port,
+ const uint8_t *dest_arp, int ifindex, bool bcast)
{
struct sockaddr_ll dest;
struct ip_udp_dhcp_packet packet;
@@ -529,7 +530,8 @@ int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
if (fd < 0)
return -errno;
- dhcp_pkt->flags |= htons(BROADCAST_FLAG);
+ if (bcast)
+ dhcp_pkt->flags |= htons(BROADCAST_FLAG);
memset(&dest, 0, sizeof(dest));
memset(&packet, 0, sizeof(packet));
diff --git a/gdhcp/common.h b/gdhcp/common.h
index e4a4251..c692799 100644
--- a/gdhcp/common.h
+++ b/gdhcp/common.h
@@ -196,7 +196,8 @@ void dhcpv6_init_header(struct dhcpv6_packet *packet,
uint8_t type);
int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port,
- const uint8_t *dest_arp, int ifindex);
+ const uint8_t *dest_arp, int ifindex,
+ bool bcast);
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,
diff --git a/gdhcp/server.c b/gdhcp/server.c
index 728992d..aa40488 100644
--- a/gdhcp/server.c
+++ b/gdhcp/server.c
@@ -529,7 +529,7 @@ static void send_packet_to_client(GDHCPServer *dhcp_server,
dhcp_send_raw_packet(dhcp_pkt,
dhcp_server->server_nip, SERVER_PORT,
ciaddr, CLIENT_PORT, chaddr,
- dhcp_server->ifindex);
+ dhcp_server->ifindex, false);
}
static void send_offer(GDHCPServer *dhcp_server,
@@ -626,7 +626,7 @@ static void send_NAK(GDHCPServer *dhcp_server,
dhcp_send_raw_packet(&packet,
dhcp_server->server_nip, SERVER_PORT,
INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
- dhcp_server->ifindex);
+ dhcp_server->ifindex, false);
}
static void send_inform(GDHCPServer *dhcp_server,
--
1.8.3.1
_______________________________________________
connman mailing list
[email protected]
https://lists.connman.net/mailman/listinfo/connman