dhcp: Don't use get_source_addr() to determine source address

That method is subject to interface filtering, which isn't ideal for
DHCP traffic that probably uses an internal interface on which the IKE
daemon might be disabled.  In that case `giaddr` is set to an incorrect
public IP, which in turn might prevent the plugin from receiving the
DHCP server's unicast response, in particular if the DHCP socket
is bound to the internal interface.

This new approach connects the client socket and thereby determines the
source address to reach the DHCP server.

Closes strongswan/strongswan#1573
This commit is contained in:
Tobias Brunner 2023-03-10 10:43:24 +01:00
parent 8bf683c469
commit 3c8887326a

View File

@ -111,6 +111,11 @@ struct private_dhcp_socket_t {
* Force configured destination address
*/
bool force_dst;
/**
* Source IP if destination address is unicast
*/
struct sockaddr_in src;
};
/**
@ -202,7 +207,6 @@ static int prepare_dhcp(private_dhcp_socket_t *this,
identification_t *identity;
dhcp_option_t *option;
int optlen = 0, remaining;
host_t *src;
uint32_t id;
memset(dhcp, 0, sizeof(*dhcp));
@ -219,14 +223,8 @@ static int prepare_dhcp(private_dhcp_socket_t *this,
}
else
{
/* act as relay agent */
src = charon->kernel->get_source_addr(charon->kernel, this->dst, NULL);
if (src)
{
memcpy(&dhcp->gateway_address, src->get_address(src).ptr,
memcpy(&dhcp->gateway_address, &this->src.sin_addr,
sizeof(dhcp->gateway_address));
src->destroy(src);
}
}
identity = transaction->get_identity(transaction);
@ -736,6 +734,7 @@ dhcp_socket_t *dhcp_socket_create()
.s_addr = INADDR_ANY,
},
};
socklen_t addr_len;
char *iface;
int on = 1, rcvbuf = 0;
struct sock_filter dhcp_filter_code[] = {
@ -888,6 +887,25 @@ dhcp_socket_t *dhcp_socket_create()
return NULL;
}
}
if (!is_broadcast(this->dst))
{
if (connect(this->send, this->dst->get_sockaddr(this->dst),
*this->dst->get_sockaddr_len(this->dst)) < 0)
{
DBG1(DBG_CFG, "unable to connect DHCP send socket: %s",
strerror(errno));
destroy(this);
return NULL;
}
addr_len = sizeof(this->src);
if (getsockname(this->send, &this->src, &addr_len) < 0)
{
DBG1(DBG_CFG, "unable to determine source address for DHCP: %s",
strerror(errno));
destroy(this);
return NULL;
}
}
lib->watcher->add(lib->watcher, this->receive, WATCHER_READ,
(watcher_cb_t)receive_dhcp, this);