Skip to content

Commit 5fb3c09

Browse files
authored
Merge pull request #3187 from s09289728096/fix_dhcp
dhcp: Fix DHCP_OFFER/DHCP_ACK destinaton.
2 parents f6bec10 + 6fd9457 commit 5fb3c09

File tree

1 file changed

+63
-3
lines changed

1 file changed

+63
-3
lines changed

lib/networking/dhserver.c

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,69 @@ int fill_options(void *dest,
227227
return ptr - (uint8_t *)dest;
228228
}
229229

230+
231+
/*
232+
* RFC 2131 Section 4.1 compliant destination address selection
233+
*/
234+
static ip_addr_t get_dhcp_destination(struct netif *netif, const DHCP_TYPE *dhcp,
235+
const ip4_addr_t *yiaddr, bool is_nak)
236+
{
237+
ip4_addr_t giaddr = get_ip(dhcp->dp_giaddr);
238+
ip4_addr_t ciaddr = get_ip(dhcp->dp_ciaddr);
239+
bool giaddr_zero = ip4_addr_isany_val(giaddr);
240+
bool ciaddr_zero = ip4_addr_isany_val(ciaddr);
241+
bool broadcast_flag = (dhcp->dp_flags & htons(0x8000)) != 0;
242+
ip_addr_t dest_addr;
243+
244+
if (!giaddr_zero) {
245+
// If giaddr is not zero, send to giaddr (relay agent)
246+
ip_addr_set_ip4_u32(&dest_addr, giaddr.addr);
247+
return dest_addr;
248+
}
249+
250+
if (is_nak) {
251+
// RFC 2131: "In all cases, when 'giaddr' is zero,
252+
// the server broadcasts any DHCPNAK messages to 0xffffffff"
253+
goto dest_broadcast;
254+
}
255+
256+
if (!ciaddr_zero) {
257+
// RFC 2131: "If the 'giaddr' field is zero and the 'ciaddr' field is nonzero,
258+
// then the server unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'"
259+
ip_addr_set_ip4_u32(&dest_addr, ciaddr.addr);
260+
return dest_addr;
261+
}
262+
263+
if (broadcast_flag) {
264+
// RFC 2131: "If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is set,
265+
// then the server broadcasts DHCPOFFER and DHCPACK messages to 0xffffffff"
266+
goto dest_broadcast;
267+
}
268+
269+
// RFC 2131: "If the broadcast bit is not set and 'giaddr' is zero and 'ciaddr' is zero,
270+
// then the server unicasts DHCPOFFER and DHCPACK messages to the client's hardware
271+
// address and 'yiaddr' address"
272+
if (yiaddr && !ip4_addr_isany(yiaddr)) {
273+
ip_addr_set_ip4_u32(&dest_addr, yiaddr->addr);
274+
// TODO: This requires ARP table manipulation to associate yiaddr with client MAC
275+
// For now, fall back to broadcast as this is complex to implement correctly
276+
goto dest_broadcast;
277+
}
278+
279+
dest_broadcast:
280+
ip_addr_set_ip4_u32(&dest_addr,
281+
ip4_addr_get_u32(netif_ip4_addr(netif)) | ~ip4_addr_get_u32(netif_ip4_netmask(netif)));
282+
return dest_addr;
283+
284+
}
285+
230286
static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
231287
{
232288
uint8_t *ptr;
233289
dhcp_entry_t *entry;
234290
struct pbuf *pp;
235291
struct netif *netif = netif_get_by_index(p->if_idx);
292+
ip_addr_t dest_addr;
236293

237294
(void)arg;
238295
(void)addr;
@@ -254,7 +311,6 @@ static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const
254311
entry = entry_by_mac(dhcp_data.dp_chaddr);
255312
if (entry == NULL) entry = vacant_address();
256313
if (entry == NULL) break;
257-
258314
dhcp_data.dp_op = 2; /* reply */
259315
dhcp_data.dp_secs = 0;
260316
dhcp_data.dp_flags = 0;
@@ -275,7 +331,9 @@ static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const
275331
pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
276332
if (pp == NULL) break;
277333
memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
278-
udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
334+
// RFC 2131 compliant destination selection for DHCP OFFER
335+
dest_addr = get_dhcp_destination(netif, &dhcp_data, &entry->addr, false);
336+
udp_sendto(upcb, pp, &dest_addr, port);
279337
pbuf_free(pp);
280338
break;
281339

@@ -319,7 +377,9 @@ static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const
319377
if (pp == NULL) break;
320378
memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
321379
memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
322-
udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
380+
// RFC 2131 compliant destination selection for DHCP ACK
381+
dest_addr = get_dhcp_destination(netif, &dhcp_data, &entry->addr, false);
382+
udp_sendto(upcb, pp, &dest_addr, port);
323383
pbuf_free(pp);
324384
break;
325385

0 commit comments

Comments
 (0)