mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-26 19:39:53 +03:00
Virtual: fix race condition in DHCP server which resulted in multiple clients receiving the same IP
A race condition in the DHCP server caused it to offer the same IP address to multiple clients when they connected at the same time, because an offered IP address was considered free until the final step (DHCP_ACK). This commit introduces a list to keep track of the pending leases created during DHCP_OFFER, so that an IP address is guaranteed to be offered to a single client.
This commit is contained in:
parent
831c907512
commit
d6cf1b85a9
@ -8956,8 +8956,8 @@ void FreeDhcpServer(VH *v)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the all lease entries
|
// Empty the leases lists
|
||||||
for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
|
for (i = 0; i < LIST_NUM(v->DhcpLeaseList); ++i)
|
||||||
{
|
{
|
||||||
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
|
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
|
||||||
FreeDhcpLease(d);
|
FreeDhcpLease(d);
|
||||||
@ -8965,6 +8965,15 @@ void FreeDhcpServer(VH *v)
|
|||||||
|
|
||||||
ReleaseList(v->DhcpLeaseList);
|
ReleaseList(v->DhcpLeaseList);
|
||||||
v->DhcpLeaseList = NULL;
|
v->DhcpLeaseList = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < LIST_NUM(v->DhcpPendingLeaseList); ++i)
|
||||||
|
{
|
||||||
|
DHCP_LEASE *d = LIST_DATA(v->DhcpPendingLeaseList, i);
|
||||||
|
FreeDhcpLease(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseList(v->DhcpPendingLeaseList);
|
||||||
|
v->DhcpPendingLeaseList = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the DHCP server
|
// Initialize the DHCP server
|
||||||
@ -8978,6 +8987,29 @@ void InitDhcpServer(VH *v)
|
|||||||
|
|
||||||
// Create a list
|
// Create a list
|
||||||
v->DhcpLeaseList = NewList(CompareDhcpLeaseList);
|
v->DhcpLeaseList = NewList(CompareDhcpLeaseList);
|
||||||
|
v->DhcpPendingLeaseList = NewList(CompareDhcpLeaseList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a pending DHCP lease item by the IP address
|
||||||
|
DHCP_LEASE *SearchDhcpPendingLeaseByIp(VH *v, UINT ip)
|
||||||
|
{
|
||||||
|
UINT i;
|
||||||
|
// Validate arguments
|
||||||
|
if (v == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < LIST_NUM(v->DhcpPendingLeaseList); ++i)
|
||||||
|
{
|
||||||
|
DHCP_LEASE *d = LIST_DATA(v->DhcpPendingLeaseList, i);
|
||||||
|
if (d->IpAddress == ip)
|
||||||
|
{
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for a DHCP lease item by the IP address
|
// Search for a DHCP lease item by the IP address
|
||||||
@ -8990,7 +9022,7 @@ DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
|
for (i = 0; i < LIST_NUM(v->DhcpLeaseList); ++i)
|
||||||
{
|
{
|
||||||
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
|
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
|
||||||
if (d->IpAddress == ip)
|
if (d->IpAddress == ip)
|
||||||
@ -9002,6 +9034,22 @@ DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search for a pending DHCP lease item by the MAC address
|
||||||
|
DHCP_LEASE *SearchDhcpPendingLeaseByMac(VH *v, UCHAR *mac)
|
||||||
|
{
|
||||||
|
DHCP_LEASE *d, t;
|
||||||
|
// Validate arguments
|
||||||
|
if (v == NULL || mac == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Copy(&t.MacAddress, mac, 6);
|
||||||
|
d = Search(v->DhcpPendingLeaseList, &t);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
// Search for a DHCP lease item by the MAC address
|
// Search for a DHCP lease item by the MAC address
|
||||||
DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac)
|
DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac)
|
||||||
{
|
{
|
||||||
@ -9099,9 +9147,8 @@ void PollingDhcpServer(VH *v)
|
|||||||
}
|
}
|
||||||
v->LastDhcpPolling = v->Now;
|
v->LastDhcpPolling = v->Now;
|
||||||
|
|
||||||
// Remove expired entries
|
LIST_CLEANUP:
|
||||||
FIRST_LIST:
|
for (i = 0; i < LIST_NUM(v->DhcpLeaseList); ++i)
|
||||||
for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)
|
|
||||||
{
|
{
|
||||||
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
|
DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);
|
||||||
|
|
||||||
@ -9109,7 +9156,21 @@ FIRST_LIST:
|
|||||||
{
|
{
|
||||||
FreeDhcpLease(d);
|
FreeDhcpLease(d);
|
||||||
Delete(v->DhcpLeaseList, d);
|
Delete(v->DhcpLeaseList, d);
|
||||||
goto FIRST_LIST;
|
goto LIST_CLEANUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PENDING_LIST_CLEANUP:
|
||||||
|
// Remove expired entries
|
||||||
|
for (i = 0; i < LIST_NUM(v->DhcpPendingLeaseList); ++i)
|
||||||
|
{
|
||||||
|
DHCP_LEASE *d = LIST_DATA(v->DhcpPendingLeaseList, i);
|
||||||
|
|
||||||
|
if (d->ExpireTime < v->Now)
|
||||||
|
{
|
||||||
|
FreeDhcpLease(d);
|
||||||
|
Delete(v->DhcpPendingLeaseList, d);
|
||||||
|
goto PENDING_LIST_CLEANUP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9151,6 +9212,11 @@ UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip)
|
|||||||
{
|
{
|
||||||
// IP address is specified
|
// IP address is specified
|
||||||
DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip);
|
DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip);
|
||||||
|
if (d == NULL)
|
||||||
|
{
|
||||||
|
d = SearchDhcpPendingLeaseByIp(v, request_ip);
|
||||||
|
}
|
||||||
|
|
||||||
if (d != NULL)
|
if (d != NULL)
|
||||||
{
|
{
|
||||||
// If an entry for the same IP address already exists,
|
// If an entry for the same IP address already exists,
|
||||||
@ -9187,6 +9253,11 @@ UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip)
|
|||||||
// If there is any entry with the same MAC address
|
// If there is any entry with the same MAC address
|
||||||
// that are already registered, use it with priority
|
// that are already registered, use it with priority
|
||||||
DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac);
|
DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac);
|
||||||
|
if (d == NULL)
|
||||||
|
{
|
||||||
|
d = SearchDhcpPendingLeaseByMac(v, mac);
|
||||||
|
}
|
||||||
|
|
||||||
if (d != NULL)
|
if (d != NULL)
|
||||||
{
|
{
|
||||||
// Examine whether the found IP address is in the allocation region
|
// Examine whether the found IP address is in the allocation region
|
||||||
@ -9234,7 +9305,7 @@ UINT GetFreeDhcpIpAddress(VH *v)
|
|||||||
for (i = ip_start; i <= ip_end;i++)
|
for (i = ip_start; i <= ip_end;i++)
|
||||||
{
|
{
|
||||||
UINT ip = Endian32(i);
|
UINT ip = Endian32(i);
|
||||||
if (SearchDhcpLeaseByIp(v, ip) == NULL)
|
if (SearchDhcpLeaseByIp(v, ip) == NULL && SearchDhcpPendingLeaseByIp(v, ip) == NULL)
|
||||||
{
|
{
|
||||||
// A free IP address is found
|
// A free IP address is found
|
||||||
return ip;
|
return ip;
|
||||||
@ -9284,7 +9355,7 @@ UINT GetFreeDhcpIpAddressByRandom(VH *v, UCHAR *mac)
|
|||||||
|
|
||||||
new_ip = Endian32(ip_start + (rand_int % (ip_end - ip_start + 1)));
|
new_ip = Endian32(ip_start + (rand_int % (ip_end - ip_start + 1)));
|
||||||
|
|
||||||
if (SearchDhcpLeaseByIp(v, new_ip) == NULL)
|
if (SearchDhcpLeaseByIp(v, new_ip) == NULL && SearchDhcpPendingLeaseByIp(v, new_ip) == NULL)
|
||||||
{
|
{
|
||||||
// A free IP address is found
|
// A free IP address is found
|
||||||
return new_ip;
|
return new_ip;
|
||||||
@ -9401,8 +9472,9 @@ void VirtualDhcpServer(VH *v, PKT *p)
|
|||||||
if (opt->Opcode == DHCP_REQUEST)
|
if (opt->Opcode == DHCP_REQUEST)
|
||||||
{
|
{
|
||||||
DHCP_LEASE *d;
|
DHCP_LEASE *d;
|
||||||
char mac[MAX_SIZE];
|
char client_mac[MAX_SIZE];
|
||||||
char str[MAX_SIZE];
|
char client_ip[MAX_SIZE];
|
||||||
|
|
||||||
// Remove old records with the same IP address
|
// Remove old records with the same IP address
|
||||||
d = SearchDhcpLeaseByIp(v, ip);
|
d = SearchDhcpLeaseByIp(v, ip);
|
||||||
if (d != NULL)
|
if (d != NULL)
|
||||||
@ -9411,17 +9483,22 @@ void VirtualDhcpServer(VH *v, PKT *p)
|
|||||||
Delete(v->DhcpLeaseList, d);
|
Delete(v->DhcpLeaseList, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d = SearchDhcpPendingLeaseByIp(v, ip);
|
||||||
|
if (d != NULL)
|
||||||
|
{
|
||||||
|
FreeDhcpLease(d);
|
||||||
|
Delete(v->DhcpPendingLeaseList, d);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new entry
|
// Create a new entry
|
||||||
d = NewDhcpLease(v->DhcpExpire, p->MacAddressSrc,
|
d = NewDhcpLease(v->DhcpExpire, p->MacAddressSrc, ip, v->DhcpMask, opt->Hostname);
|
||||||
ip, v->DhcpMask,
|
|
||||||
opt->Hostname);
|
|
||||||
d->Id = ++v->DhcpId;
|
d->Id = ++v->DhcpId;
|
||||||
Add(v->DhcpLeaseList, d);
|
Add(v->DhcpLeaseList, d);
|
||||||
MacToStr(mac, sizeof(mac), d->MacAddress);
|
|
||||||
|
|
||||||
IPToStr32(str, sizeof(str), d->IpAddress);
|
MacToStr(client_mac, sizeof(client_mac), d->MacAddress);
|
||||||
|
IPToStr32(client_ip, sizeof(client_ip), d->IpAddress);
|
||||||
|
|
||||||
NLog(v, "LH_NAT_DHCP_CREATED", d->Id, mac, str, d->Hostname, v->DhcpExpire / 1000);
|
NLog(v, "LH_NAT_DHCP_CREATED", d->Id, client_mac, client_ip, d->Hostname, v->DhcpExpire / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond
|
// Respond
|
||||||
@ -9510,12 +9587,25 @@ void VirtualDhcpServer(VH *v, PKT *p)
|
|||||||
char client_mac[MAX_SIZE];
|
char client_mac[MAX_SIZE];
|
||||||
char client_ip[64];
|
char client_ip[64];
|
||||||
IP ips;
|
IP ips;
|
||||||
|
|
||||||
BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6);
|
BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6);
|
||||||
UINTToIP(&ips, ip);
|
UINTToIP(&ips, ip);
|
||||||
IPToStr(client_ip, sizeof(client_ip), &ips);
|
IPToStr(client_ip, sizeof(client_ip), &ips);
|
||||||
Debug("DHCP %s : %s given %s\n",
|
|
||||||
ret.Opcode == DHCP_OFFER ? "DHCP_OFFER" : "DHCP_ACK",
|
if (ret.Opcode == DHCP_OFFER)
|
||||||
client_mac, client_ip);
|
{
|
||||||
|
// DHCP_OFFER
|
||||||
|
DHCP_LEASE *d = NewDhcpLease(5000, p->MacAddressSrc, ip, v->DhcpMask, opt->Hostname);
|
||||||
|
d->Id = LIST_NUM(v->DhcpPendingLeaseList);
|
||||||
|
Add(v->DhcpPendingLeaseList, d);
|
||||||
|
|
||||||
|
Debug("VirtualDhcpServer(): %s has been marked as pending for %s\n", client_ip, client_mac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// DHCP_ACK
|
||||||
|
Debug("VirtualDhcpServer(): %s has been assigned to %s\n", client_ip, client_mac);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a DHCP option
|
// Build a DHCP option
|
||||||
|
@ -296,6 +296,7 @@ struct VH
|
|||||||
UINT DhcpDns2; // DNS server address 2
|
UINT DhcpDns2; // DNS server address 2
|
||||||
char DhcpDomain[MAX_HOST_NAME_LEN + 1]; // Assigned domain name
|
char DhcpDomain[MAX_HOST_NAME_LEN + 1]; // Assigned domain name
|
||||||
LIST *DhcpLeaseList; // DHCP lease list
|
LIST *DhcpLeaseList; // DHCP lease list
|
||||||
|
LIST *DhcpPendingLeaseList; // Pending DHCP lease list
|
||||||
UINT64 LastDhcpPolling; // Time which the DHCP list polled last
|
UINT64 LastDhcpPolling; // Time which the DHCP list polled last
|
||||||
bool SaveLog; // Save a log
|
bool SaveLog; // Save a log
|
||||||
DHCP_CLASSLESS_ROUTE_TABLE PushRoute; // Pushing routing table
|
DHCP_CLASSLESS_ROUTE_TABLE PushRoute; // Pushing routing table
|
||||||
@ -511,7 +512,9 @@ int CompareDhcpLeaseList(void *p1, void *p2);
|
|||||||
DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname);
|
DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname);
|
||||||
void FreeDhcpLease(DHCP_LEASE *d);
|
void FreeDhcpLease(DHCP_LEASE *d);
|
||||||
DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac);
|
DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac);
|
||||||
|
DHCP_LEASE *SearchDhcpPendingLeaseByMac(VH *v, UCHAR *mac);
|
||||||
DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip);
|
DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip);
|
||||||
|
DHCP_LEASE *SearchDhcpPendingLeaseByIp(VH *v, UINT ip);
|
||||||
UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip);
|
UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip);
|
||||||
UINT GetFreeDhcpIpAddress(VH *v);
|
UINT GetFreeDhcpIpAddress(VH *v);
|
||||||
UINT GetFreeDhcpIpAddressByRandom(VH *v, UCHAR *mac);
|
UINT GetFreeDhcpIpAddressByRandom(VH *v, UCHAR *mac);
|
||||||
|
Loading…
Reference in New Issue
Block a user