n-dhcp4/connection: dynamically allocate the receive buffer

Each connection object includes a 64KiB scratch buffer used for
receiving packets. When many instances of the client are created,
those buffers use a significant amount of memory. For example, 500
clients take ~30MiB of memory constantly reserved only for those
buffers.

Since the buffer is used only in the function and is never passed
outside, a stack allocation would suffice; however, it's not wise to
do such large allocations on the stack; dynamically allocate it.

https://github.com/nettools/n-dhcp4/issues/26
https://github.com/nettools/n-dhcp4/pull/27

64513e31c0
This commit is contained in:
Beniamino Galvani
2022-03-14 11:05:28 +01:00
committed by Thomas Haller
parent 197e73ac7c
commit a5a5654f18
2 changed files with 11 additions and 15 deletions

View File

@@ -1147,16 +1147,21 @@ int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection,
int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
NDhcp4Incoming **messagep) { NDhcp4Incoming **messagep) {
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
_c_cleanup_(c_freep) uint8_t *buffer = NULL;
char serv_addr[INET_ADDRSTRLEN]; char serv_addr[INET_ADDRSTRLEN];
char client_addr[INET_ADDRSTRLEN]; char client_addr[INET_ADDRSTRLEN];
uint8_t type = 0; uint8_t type = 0;
int r; int r;
buffer = malloc(UINT16_MAX);
if (!buffer)
return -ENOMEM;
switch (connection->state) { switch (connection->state) {
case N_DHCP4_C_CONNECTION_STATE_PACKET: case N_DHCP4_C_CONNECTION_STATE_PACKET:
r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, r = n_dhcp4_c_socket_packet_recv(connection->fd_packet,
connection->scratch_buffer, buffer,
sizeof(connection->scratch_buffer), UINT16_MAX,
&message); &message);
if (!r) if (!r)
break; break;
@@ -1165,8 +1170,8 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
return N_DHCP4_E_AGAIN; return N_DHCP4_E_AGAIN;
case N_DHCP4_C_CONNECTION_STATE_DRAINING: case N_DHCP4_C_CONNECTION_STATE_DRAINING:
r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, r = n_dhcp4_c_socket_packet_recv(connection->fd_packet,
connection->scratch_buffer, buffer,
sizeof(connection->scratch_buffer), UINT16_MAX,
&message); &message);
if (!r) if (!r)
break; break;
@@ -1188,8 +1193,8 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
/* fall-through */ /* fall-through */
case N_DHCP4_C_CONNECTION_STATE_UDP: case N_DHCP4_C_CONNECTION_STATE_UDP:
r = n_dhcp4_c_socket_udp_recv(connection->fd_udp, r = n_dhcp4_c_socket_udp_recv(connection->fd_udp,
connection->scratch_buffer, buffer,
sizeof(connection->scratch_buffer), UINT16_MAX,
&message); &message);
if (!r) if (!r)
break; break;

View File

@@ -334,15 +334,6 @@ struct NDhcp4CConnection {
uint32_t client_ip; /* client IP address, or 0 */ uint32_t client_ip; /* client IP address, or 0 */
uint32_t server_ip; /* server IP address, or 0 */ uint32_t server_ip; /* server IP address, or 0 */
uint16_t mtu; /* client mtu, or 0 */ uint16_t mtu; /* client mtu, or 0 */
/*
* When we get DHCP packets from the kernel, we need a buffer to read
* the data into. Since UDP packets can be up to 2^16 bytes in size, we
* avoid placing it on the stack and instead read into this scratch
* buffer. It is purely meant as stack replacement, no data is returned
* through this buffer.
*/
uint8_t scratch_buffer[UINT16_MAX];
}; };
#define N_DHCP4_C_CONNECTION_NULL(_x) { \ #define N_DHCP4_C_CONNECTION_NULL(_x) { \