1/*
2 * Copyright 2006-2012, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		Alexander von Gluck, kallisti5@unixzen.com
8 */
9
10
11#include "AutoconfigLooper.h"
12
13#include <errno.h>
14#include <net/if_dl.h>
15#include <net/if_media.h>
16#include <net/if_types.h>
17#include <stdio.h>
18#include <sys/socket.h>
19#include <sys/sockio.h>
20
21#include <NetworkInterface.h>
22#include <NetworkNotifications.h>
23
24#include "DHCPClient.h"
25#include "NetServer.h"
26
27
28static const uint32 kMsgReadyToRun = 'rdyr';
29
30
31AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device)
32	: BLooper(device),
33	fTarget(target),
34	fDevice(device),
35	fCurrentClient(NULL),
36	fLastMediaStatus(0),
37	fJoiningNetwork(false)
38{
39	BMessage ready(kMsgReadyToRun);
40	PostMessage(&ready);
41}
42
43
44AutoconfigLooper::~AutoconfigLooper()
45{
46}
47
48
49void
50AutoconfigLooper::_RemoveClient()
51{
52	if (fCurrentClient == NULL)
53		return;
54
55	RemoveHandler(fCurrentClient);
56	delete fCurrentClient;
57	fCurrentClient = NULL;
58}
59
60
61void
62AutoconfigLooper::_ConfigureIPv4()
63{
64	// start with DHCP
65
66	if (fCurrentClient == NULL) {
67		fCurrentClient = new DHCPClient(fTarget, fDevice.String());
68		AddHandler(fCurrentClient);
69	}
70
71	// set IFF_CONFIGURING flag on interface
72
73	BNetworkInterface interface(fDevice.String());
74	int32 flags = interface.Flags() & ~IFF_AUTO_CONFIGURED;
75	interface.SetFlags(flags | IFF_CONFIGURING);
76
77	if (fCurrentClient->Initialize() == B_OK)
78		return;
79
80	_RemoveClient();
81
82	puts("DHCP failed miserably!");
83
84	// DHCP obviously didn't work out, take some default values for now
85	// TODO: have a look at zeroconf
86	// TODO: this could also be done add-on based
87
88	if ((interface.Flags() & IFF_CONFIGURING) == 0) {
89		// Someone else configured the interface in the mean time
90		return;
91	}
92
93	BMessage message(kMsgConfigureInterface);
94	message.AddString("device", fDevice.String());
95	message.AddBool("auto_configured", true);
96
97	BNetworkAddress link;
98	uint8 last = 56;
99	if (interface.GetHardwareAddress(link) == B_OK) {
100		// choose IP address depending on the MAC address, if available
101		uint8* mac = link.LinkLevelAddress();
102		last = mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5];
103		if (last > 253)
104			last = 253;
105		else if (last == 0)
106			last = 1;
107	}
108
109	// IANA defined the default autoconfig network (for when a DHCP request
110	// fails for some reason) as being 169.254.0.0/255.255.0.0. We are only
111	// generating the last octet but we could also use the 2 last octets if
112	// wanted.
113	char string[64];
114	snprintf(string, sizeof(string), "169.254.0.%u", last);
115
116	BMessage address;
117	address.AddInt32("family", AF_INET);
118	address.AddString("address", string);
119	message.AddMessage("address", &address);
120
121	fTarget.SendMessage(&message);
122}
123
124
125void
126AutoconfigLooper::_ReadyToRun()
127{
128	start_watching_network(
129		B_WATCH_NETWORK_LINK_CHANGES | B_WATCH_NETWORK_WLAN_CHANGES, this);
130
131	BNetworkInterface interface(fDevice.String());
132	if (interface.HasLink()) {
133		_ConfigureIPv4();
134		//_ConfigureIPv6();	// TODO: router advertisement and dhcpv6
135
136		// Also make sure we don't spuriously try to configure again from
137		// a link changed notification that might race us.
138		fLastMediaStatus |= IFM_ACTIVE;
139	}
140}
141
142
143void
144AutoconfigLooper::_NetworkMonitorNotification(BMessage* message)
145{
146	int32 opcode;
147	BString device;
148	if (message->FindString("device", &device) != B_OK) {
149		if (message->FindString("interface", &device) != B_OK)
150			return;
151
152		// TODO: Clean this mess up. Wireless devices currently use their
153		// "device_name" in the interface field. First of all the
154		// joins/leaves/scans should be device, not interface specific, so
155		// the field should be changed. Then the device_name as seen by the
156		// driver is missing the "/dev" part, as it is a relative path within
157		// "/dev". On the other hand the net stack uses names that include
158		// "/dev" as it uses them to open the fds, hence a full absolute path.
159		// Note that the wpa_supplicant does the same workaround as we do here
160		// to build an interface name, so that has to be changed as well when
161		// this is fixed.
162		device.Prepend("/dev/");
163	}
164
165	if (device != fDevice || message->FindInt32("opcode", &opcode) != B_OK)
166		return;
167
168	switch (opcode) {
169		case B_NETWORK_DEVICE_LINK_CHANGED:
170		{
171			int32 media;
172			if (message->FindInt32("media", &media) != B_OK)
173				break;
174
175			if ((fLastMediaStatus & IFM_ACTIVE) == 0
176				&& (media & IFM_ACTIVE) != 0) {
177				// Reconfigure the interface when we have a link again
178				_ConfigureIPv4();
179				//_ConfigureIPv6();	// TODO: router advertisement and dhcpv6
180			}
181
182			fLastMediaStatus = media;
183			break;
184		}
185
186		case B_NETWORK_WLAN_SCANNED:
187		{
188			if (fJoiningNetwork || (fLastMediaStatus & IFM_ACTIVE) != 0) {
189				// We already have a link or are already joining.
190				break;
191			}
192
193			fJoiningNetwork = true;
194				// TODO: For now we never reset this flag. We can only do that
195				// after infrastructure has been added to discern a scan reason.
196				// If we would always auto join we would possibly interfere
197				// with active scans in the process of connecting to an AP
198				// either for the initial connection, or after connection loss
199				// to re-establish the link.
200
201			BMessage message(kMsgAutoJoinNetwork);
202			message.AddString("device", fDevice);
203			fTarget.SendMessage(&message);
204			break;
205		}
206	}
207}
208
209
210void
211AutoconfigLooper::MessageReceived(BMessage* message)
212{
213	switch (message->what) {
214		case kMsgReadyToRun:
215			_ReadyToRun();
216			break;
217
218		case B_NETWORK_MONITOR:
219			_NetworkMonitorNotification(message);
220			break;
221
222		default:
223			BLooper::MessageReceived(message);
224			break;
225	}
226}
227
228