* Copyright 2006-2012, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel DΓΆrfler, axeld@pinc-software.de
* Alexander von Gluck, kallisti5@unixzen.com
*/
#include "AutoconfigLooper.h"
#include <errno.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <NetworkInterface.h>
#include <NetworkNotifications.h>
#include "DHCPClient.h"
#include "NetServer.h"
static const uint32 kMsgReadyToRun = 'rdyr';
AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device)
: BLooper(device),
fTarget(target),
fDevice(device),
fCurrentClient(NULL),
fLastMediaStatus(0),
fJoiningNetwork(false)
{
BMessage ready(kMsgReadyToRun);
PostMessage(&ready);
}
AutoconfigLooper::~AutoconfigLooper()
{
_RemoveClient();
}
void
AutoconfigLooper::_RemoveClient()
{
if (fCurrentClient == NULL)
return;
delete fCurrentClient;
fCurrentClient = NULL;
}
void
AutoconfigLooper::_ConfigureIPv4()
{
if (fCurrentClient == NULL) {
fCurrentClient = new DHCPClient(fTarget, fDevice.String());
AddHandler(fCurrentClient);
}
BNetworkInterface interface(fDevice.String());
int32 flags = interface.Flags() & ~IFF_AUTO_CONFIGURED;
interface.SetFlags(flags | IFF_CONFIGURING);
if (fCurrentClient->Start() == B_OK)
return;
_ConfigureIPv4Failed();
}
void
AutoconfigLooper::_ConfigureIPv4Failed()
{
_RemoveClient();
puts("DHCP failed miserably!");
BNetworkInterface interface(fDevice.String());
if ((interface.Flags() & IFF_CONFIGURING) == 0) {
return;
}
BMessage message(kMsgConfigureInterface);
message.AddString("device", fDevice.String());
message.AddBool("auto_configured", true);
BNetworkAddress link;
uint8 last = 56;
if (interface.GetHardwareAddress(link) == B_OK) {
uint8* mac = link.LinkLevelAddress();
last = mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5];
if (last > 253)
last = 253;
else if (last == 0)
last = 1;
}
char string[64];
snprintf(string, sizeof(string), "169.254.0.%u", last);
BMessage address;
address.AddInt32("family", AF_INET);
address.AddString("address", string);
message.AddMessage("address", &address);
fTarget.SendMessage(&message);
}
void
AutoconfigLooper::_ReadyToRun()
{
start_watching_network(
B_WATCH_NETWORK_LINK_CHANGES | B_WATCH_NETWORK_WLAN_CHANGES, this);
BNetworkInterface interface(fDevice.String());
if (interface.HasLink()) {
_ConfigureIPv4();
fLastMediaStatus |= IFM_ACTIVE;
}
}
void
AutoconfigLooper::_NetworkMonitorNotification(BMessage* message)
{
int32 opcode;
BString device;
if (message->FindString("device", &device) != B_OK) {
if (message->FindString("interface", &device) != B_OK)
return;
device.Prepend("/dev/");
}
if (device != fDevice || message->FindInt32("opcode", &opcode) != B_OK)
return;
switch (opcode) {
case B_NETWORK_DEVICE_LINK_CHANGED:
{
int32 media;
if (message->FindInt32("media", &media) != B_OK)
break;
if ((fLastMediaStatus & IFM_ACTIVE) == 0 && (media & IFM_ACTIVE) != 0) {
_ConfigureIPv4();
} else if ((media & IFM_ACTIVE) == 0) {
_RemoveClient();
}
fLastMediaStatus = media;
break;
}
case B_NETWORK_WLAN_SCANNED:
{
if (fJoiningNetwork || (fLastMediaStatus & IFM_ACTIVE) != 0) {
break;
}
fJoiningNetwork = true;
BMessage message(kMsgAutoJoinNetwork);
message.AddString("device", fDevice);
fTarget.SendMessage(&message);
break;
}
}
}
void
AutoconfigLooper::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgReadyToRun:
_ReadyToRun();
break;
case kMsgAutoConfigureFailed:
_ConfigureIPv4Failed();
break;
case B_NETWORK_MONITOR:
_NetworkMonitorNotification(message);
break;
default:
BLooper::MessageReceived(message);
break;
}
}