net_server: Run DHCP negotiation on a separate thread to fix hangs.
Previously we ran DHCP negotiation on the looper thread. This meant that we just blocked it for long periods of time, which stopped configuration settings changes from working properly, and causing the Network preferences panel to hang when net_server failed to respond to its messages. Now, we spawn a separate thread to do the real work on, and unlock the looper around the recvfrom() with long timeouts. There's now a kMsgAutoConfigureFailed that is sent when auto-configuration fails, so that the AutoconfigLooper can fallback as before. Additionally, the main NetServer class quits loopers for devices on any configuration change, rather than deferring it unecessarily (and possibly wrongly, in some cases.) Fixes #18037. Probably fixes #17300 and may help with others.
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
#define kNetServerSignature "application/x-vnd.haiku-net_server"
|
#define kNetServerSignature "application/x-vnd.haiku-net_server"
|
||||||
|
|
||||||
#define kMsgConfigureInterface 'COif'
|
#define kMsgConfigureInterface 'COif'
|
||||||
|
#define kMsgAutoConfigureFailed 'COaf'
|
||||||
#define kMsgConfigureResolver 'COrs'
|
#define kMsgConfigureResolver 'COrs'
|
||||||
#define kMsgCountPersistentNetworks 'CPnw'
|
#define kMsgCountPersistentNetworks 'CPnw'
|
||||||
#define kMsgGetPersistentNetwork 'GPnw'
|
#define kMsgGetPersistentNetwork 'GPnw'
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ AutoconfigClient::~AutoconfigClient()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
AutoconfigClient::Initialize()
|
AutoconfigClient::Start()
|
||||||
{
|
{
|
||||||
return B_NOT_SUPPORTED;
|
return B_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public:
|
|||||||
BMessenger target, const char* device);
|
BMessenger target, const char* device);
|
||||||
virtual ~AutoconfigClient();
|
virtual ~AutoconfigClient();
|
||||||
|
|
||||||
virtual status_t Initialize();
|
virtual status_t Start();
|
||||||
|
|
||||||
const BMessenger& Target() const { return fTarget; }
|
const BMessenger& Target() const { return fTarget; }
|
||||||
const char* Device() const { return fDevice.String(); }
|
const char* Device() const { return fDevice.String(); }
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device)
|
|||||||
|
|
||||||
AutoconfigLooper::~AutoconfigLooper()
|
AutoconfigLooper::~AutoconfigLooper()
|
||||||
{
|
{
|
||||||
|
_RemoveClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +53,6 @@ AutoconfigLooper::_RemoveClient()
|
|||||||
if (fCurrentClient == NULL)
|
if (fCurrentClient == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RemoveHandler(fCurrentClient);
|
|
||||||
delete fCurrentClient;
|
delete fCurrentClient;
|
||||||
fCurrentClient = NULL;
|
fCurrentClient = NULL;
|
||||||
}
|
}
|
||||||
@@ -74,9 +74,16 @@ AutoconfigLooper::_ConfigureIPv4()
|
|||||||
int32 flags = interface.Flags() & ~IFF_AUTO_CONFIGURED;
|
int32 flags = interface.Flags() & ~IFF_AUTO_CONFIGURED;
|
||||||
interface.SetFlags(flags | IFF_CONFIGURING);
|
interface.SetFlags(flags | IFF_CONFIGURING);
|
||||||
|
|
||||||
if (fCurrentClient->Initialize() == B_OK)
|
if (fCurrentClient->Start() == B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_ConfigureIPv4Failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
AutoconfigLooper::_ConfigureIPv4Failed()
|
||||||
|
{
|
||||||
_RemoveClient();
|
_RemoveClient();
|
||||||
|
|
||||||
puts("DHCP failed miserably!");
|
puts("DHCP failed miserably!");
|
||||||
@@ -85,6 +92,7 @@ AutoconfigLooper::_ConfigureIPv4()
|
|||||||
// TODO: have a look at zeroconf
|
// TODO: have a look at zeroconf
|
||||||
// TODO: this could also be done add-on based
|
// TODO: this could also be done add-on based
|
||||||
|
|
||||||
|
BNetworkInterface interface(fDevice.String());
|
||||||
if ((interface.Flags() & IFF_CONFIGURING) == 0) {
|
if ((interface.Flags() & IFF_CONFIGURING) == 0) {
|
||||||
// Someone else configured the interface in the mean time
|
// Someone else configured the interface in the mean time
|
||||||
return;
|
return;
|
||||||
@@ -176,7 +184,7 @@ AutoconfigLooper::_NetworkMonitorNotification(BMessage* message)
|
|||||||
// Reconfigure the interface when we have a link again
|
// Reconfigure the interface when we have a link again
|
||||||
_ConfigureIPv4();
|
_ConfigureIPv4();
|
||||||
//_ConfigureIPv6(); // TODO: router advertisement and dhcpv6
|
//_ConfigureIPv6(); // TODO: router advertisement and dhcpv6
|
||||||
} else if ((fLastMediaStatus & IFM_ACTIVE) != 0 && (media & IFM_ACTIVE) == 0) {
|
} else if ((media & IFM_ACTIVE) == 0) {
|
||||||
_RemoveClient();
|
_RemoveClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +224,10 @@ AutoconfigLooper::MessageReceived(BMessage* message)
|
|||||||
_ReadyToRun();
|
_ReadyToRun();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case kMsgAutoConfigureFailed:
|
||||||
|
_ConfigureIPv4Failed();
|
||||||
|
break;
|
||||||
|
|
||||||
case B_NETWORK_MONITOR:
|
case B_NETWORK_MONITOR:
|
||||||
_NetworkMonitorNotification(message);
|
_NetworkMonitorNotification(message);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void _RemoveClient();
|
void _RemoveClient();
|
||||||
void _ConfigureIPv4();
|
void _ConfigureIPv4();
|
||||||
|
void _ConfigureIPv4Failed();
|
||||||
void _ReadyToRun();
|
void _ReadyToRun();
|
||||||
void _NetworkMonitorNotification(BMessage* message);
|
void _NetworkMonitorNotification(BMessage* message);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <Debug.h>
|
#include <Debug.h>
|
||||||
#include <Message.h>
|
#include <Message.h>
|
||||||
#include <MessageRunner.h>
|
#include <MessageRunner.h>
|
||||||
|
#include <Looper.h>
|
||||||
|
|
||||||
#include "NetServer.h"
|
#include "NetServer.h"
|
||||||
|
|
||||||
@@ -482,6 +483,7 @@ DHCPClient::DHCPClient(BMessenger target, const char* device)
|
|||||||
fConfiguration(kMsgConfigureInterface),
|
fConfiguration(kMsgConfigureInterface),
|
||||||
fResolverConfiguration(kMsgConfigureResolver),
|
fResolverConfiguration(kMsgConfigureResolver),
|
||||||
fRunner(NULL),
|
fRunner(NULL),
|
||||||
|
fNegotiateThread(-1),
|
||||||
fAssignedAddress(0),
|
fAssignedAddress(0),
|
||||||
fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES),
|
fServer(AF_INET, NULL, DHCP_SERVER_PORT, B_UNCONFIGURED_ADDRESS_FAMILIES),
|
||||||
fStartTime(0),
|
fStartTime(0),
|
||||||
@@ -518,18 +520,27 @@ DHCPClient::DHCPClient(BMessenger target, const char* device)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openlog_thread("DHCP", 0, LOG_DAEMON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DHCPClient::~DHCPClient()
|
DHCPClient::~DHCPClient()
|
||||||
{
|
{
|
||||||
if (fStatus != B_OK)
|
thread_id thread = fNegotiateThread;
|
||||||
return;
|
if (thread != -1) {
|
||||||
|
fNegotiateThread = -1;
|
||||||
|
suspend_thread(thread);
|
||||||
|
resume_thread(thread);
|
||||||
|
|
||||||
|
UnlockLooper();
|
||||||
|
wait_for_thread(thread, NULL);
|
||||||
|
LockLooper();
|
||||||
|
}
|
||||||
|
|
||||||
delete fRunner;
|
delete fRunner;
|
||||||
|
|
||||||
|
if (fStatus != B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
|
int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
if (socket < 0)
|
if (socket < 0)
|
||||||
return;
|
return;
|
||||||
@@ -541,23 +552,42 @@ DHCPClient::~DHCPClient()
|
|||||||
|
|
||||||
_SendMessage(socket, release, fServer);
|
_SendMessage(socket, release, fServer);
|
||||||
close(socket);
|
close(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
DHCPClient::Start()
|
||||||
|
{
|
||||||
|
if (fNegotiateThread != -1)
|
||||||
|
return EINPROGRESS;
|
||||||
|
|
||||||
|
fNegotiateThread = spawn_thread(_NegotiatorThread, "DHCP negotiator",
|
||||||
|
B_NORMAL_PRIORITY, this);
|
||||||
|
return resume_thread(fNegotiateThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
DHCPClient::_NegotiatorThread(void* data)
|
||||||
|
{
|
||||||
|
openlog_thread("DHCP", 0, LOG_DAEMON);
|
||||||
|
|
||||||
|
DHCPClient* client = (DHCPClient*)data;
|
||||||
|
client->LockLooper();
|
||||||
|
client->fStatus = client->_Negotiate();
|
||||||
|
syslog(LOG_DEBUG, "%s: DHCP status = %s\n", client->Device(), strerror(client->fStatus));
|
||||||
|
client->fNegotiateThread = -1;
|
||||||
|
client->UnlockLooper();
|
||||||
|
|
||||||
closelog_thread();
|
closelog_thread();
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
DHCPClient::Initialize()
|
DHCPClient::_Negotiate()
|
||||||
{
|
|
||||||
fStatus = _Negotiate(fAssignedAddress == 0 ? INIT : INIT_REBOOT);
|
|
||||||
syslog(LOG_DEBUG, "%s: DHCP status = %s\n", Device(), strerror(fStatus));
|
|
||||||
return fStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
DHCPClient::_Negotiate(dhcp_state state)
|
|
||||||
{
|
{
|
||||||
|
dhcp_state state = _CurrentState();
|
||||||
if (state == BOUND)
|
if (state == BOUND)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
@@ -595,7 +625,7 @@ DHCPClient::_Negotiate(dhcp_state state)
|
|||||||
bigtime_t previousLeaseTime = fLeaseTime;
|
bigtime_t previousLeaseTime = fLeaseTime;
|
||||||
|
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
while (state != BOUND) {
|
while (state != BOUND && fNegotiateThread != -1) {
|
||||||
status = _StateTransition(socket, state);
|
status = _StateTransition(socket, state);
|
||||||
if (status != B_OK && (state == SELECTING || state == REBOOTING))
|
if (status != B_OK && (state == SELECTING || state == REBOOTING))
|
||||||
break;
|
break;
|
||||||
@@ -618,8 +648,12 @@ DHCPClient::_Negotiate(dhcp_state state)
|
|||||||
_RestartLease(fRenewalTime);
|
_RestartLease(fRenewalTime);
|
||||||
|
|
||||||
fStatus = status;
|
fStatus = status;
|
||||||
if (status)
|
if (status != B_OK) {
|
||||||
|
// If we were asked to quit, don't inform the looper that we failed.
|
||||||
|
if (fNegotiateThread != -1)
|
||||||
|
Looper()->PostMessage(kMsgAutoConfigureFailed, Looper(), this);
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// configure interface
|
// configure interface
|
||||||
BMessage reply;
|
BMessage reply;
|
||||||
@@ -737,8 +771,12 @@ DHCPClient::_StateTransition(int socket, dhcp_state& state)
|
|||||||
char buffer[2048];
|
char buffer[2048];
|
||||||
struct sockaddr_in from;
|
struct sockaddr_in from;
|
||||||
socklen_t fromLength = sizeof(from);
|
socklen_t fromLength = sizeof(from);
|
||||||
|
|
||||||
|
UnlockLooper();
|
||||||
ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
|
ssize_t bytesReceived = recvfrom(socket, buffer, sizeof(buffer),
|
||||||
0, (struct sockaddr*)&from, &fromLength);
|
0, (struct sockaddr*)&from, &fromLength);
|
||||||
|
LockLooper();
|
||||||
|
|
||||||
if (bytesReceived < 0 && errno == B_TIMED_OUT) {
|
if (bytesReceived < 0 && errno == B_TIMED_OUT) {
|
||||||
// depending on the state, we'll just try again
|
// depending on the state, we'll just try again
|
||||||
if (!_TimeoutShift(socket, state, timeout))
|
if (!_TimeoutShift(socket, state, timeout))
|
||||||
@@ -1032,7 +1070,7 @@ DHCPClient::_CurrentState() const
|
|||||||
bigtime_t now = system_time();
|
bigtime_t now = system_time();
|
||||||
|
|
||||||
if (now > fLeaseTime || fStatus != B_OK)
|
if (now > fLeaseTime || fStatus != B_OK)
|
||||||
return INIT;
|
return fAssignedAddress == 0 ? INIT : INIT_REBOOT;
|
||||||
if (now >= fRebindingTime)
|
if (now >= fRebindingTime)
|
||||||
return REBINDING;
|
return REBINDING;
|
||||||
if (now >= fRenewalTime)
|
if (now >= fRenewalTime)
|
||||||
@@ -1046,7 +1084,7 @@ DHCPClient::MessageReceived(BMessage* message)
|
|||||||
{
|
{
|
||||||
switch (message->what) {
|
switch (message->what) {
|
||||||
case kMsgLeaseTime:
|
case kMsgLeaseTime:
|
||||||
_Negotiate(_CurrentState());
|
Start();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -39,12 +39,13 @@ public:
|
|||||||
const char* device);
|
const char* device);
|
||||||
virtual ~DHCPClient();
|
virtual ~DHCPClient();
|
||||||
|
|
||||||
virtual status_t Initialize();
|
virtual status_t Start();
|
||||||
|
|
||||||
virtual void MessageReceived(BMessage* message);
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _Negotiate(dhcp_state state);
|
static status_t _NegotiatorThread(void* data);
|
||||||
|
status_t _Negotiate();
|
||||||
status_t _GotMessage(dhcp_state& state,
|
status_t _GotMessage(dhcp_state& state,
|
||||||
dhcp_message* message);
|
dhcp_message* message);
|
||||||
status_t _StateTransition(int socket, dhcp_state& state);
|
status_t _StateTransition(int socket, dhcp_state& state);
|
||||||
@@ -67,6 +68,7 @@ private:
|
|||||||
BMessage fConfiguration;
|
BMessage fConfiguration;
|
||||||
BMessage fResolverConfiguration;
|
BMessage fResolverConfiguration;
|
||||||
BMessageRunner* fRunner;
|
BMessageRunner* fRunner;
|
||||||
|
thread_id fNegotiateThread;
|
||||||
uint8 fMAC[6];
|
uint8 fMAC[6];
|
||||||
BString fHostName;
|
BString fHostName;
|
||||||
uint32 fTransactionID;
|
uint32 fTransactionID;
|
||||||
|
|||||||
@@ -426,10 +426,12 @@ NetServer::_ConfigureInterface(BMessage& message)
|
|||||||
if (message.FindInt32("flags", &flags) != B_OK)
|
if (message.FindInt32("flags", &flags) != B_OK)
|
||||||
flags = IFF_UP;
|
flags = IFF_UP;
|
||||||
|
|
||||||
bool autoConfigured;
|
bool autoConfigured = false;
|
||||||
if (message.FindBool("auto_configured", &autoConfigured) == B_OK
|
if (message.FindBool("auto_configured", &autoConfigured) == B_OK
|
||||||
&& autoConfigured) {
|
&& autoConfigured) {
|
||||||
flags |= IFF_AUTO_CONFIGURED;
|
flags |= IFF_AUTO_CONFIGURED;
|
||||||
|
} else {
|
||||||
|
_QuitLooperForDevice(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 mtu;
|
int32 mtu;
|
||||||
@@ -465,10 +467,8 @@ NetServer::_ConfigureInterface(BMessage& message)
|
|||||||
&addressMessage) == B_OK; index++) {
|
&addressMessage) == B_OK; index++) {
|
||||||
BNetworkInterfaceAddressSettings addressSettings(addressMessage);
|
BNetworkInterfaceAddressSettings addressSettings(addressMessage);
|
||||||
|
|
||||||
if (addressSettings.IsAutoConfigure()) {
|
if (addressSettings.IsAutoConfigure())
|
||||||
_QuitLooperForDevice(name);
|
|
||||||
startAutoConfig = true;
|
startAutoConfig = true;
|
||||||
}
|
|
||||||
|
|
||||||
// set address/mask/broadcast/peer
|
// set address/mask/broadcast/peer
|
||||||
|
|
||||||
@@ -570,8 +570,7 @@ NetServer::_ConfigureInterface(BMessage& message)
|
|||||||
looper->Run();
|
looper->Run();
|
||||||
|
|
||||||
fDeviceMap[name] = looper;
|
fDeviceMap[name] = looper;
|
||||||
} else if (!autoConfigured)
|
}
|
||||||
_QuitLooperForDevice(name);
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user