xref: /haiku/src/servers/net/AutoconfigLooper.cpp (revision 7aba63496b2f540f760f420b6c7743b7de2b6de3)
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 
28 static const uint32 kMsgReadyToRun = 'rdyr';
29 
30 
AutoconfigLooper(BMessenger target,const char * device)31 AutoconfigLooper::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 
~AutoconfigLooper()44 AutoconfigLooper::~AutoconfigLooper()
45 {
46 }
47 
48 
49 void
_RemoveClient()50 AutoconfigLooper::_RemoveClient()
51 {
52 	if (fCurrentClient == NULL)
53 		return;
54 
55 	RemoveHandler(fCurrentClient);
56 	delete fCurrentClient;
57 	fCurrentClient = NULL;
58 }
59 
60 
61 void
_ConfigureIPv4()62 AutoconfigLooper::_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 
125 void
_ReadyToRun()126 AutoconfigLooper::_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 
143 void
_NetworkMonitorNotification(BMessage * message)144 AutoconfigLooper::_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 && (media & IFM_ACTIVE) != 0) {
176 				// Reconfigure the interface when we have a link again
177 				_ConfigureIPv4();
178 				//_ConfigureIPv6();	// TODO: router advertisement and dhcpv6
179 			} else if ((fLastMediaStatus & IFM_ACTIVE) != 0 && (media & IFM_ACTIVE) == 0) {
180 				_RemoveClient();
181 			}
182 
183 			fLastMediaStatus = media;
184 			break;
185 		}
186 
187 		case B_NETWORK_WLAN_SCANNED:
188 		{
189 			if (fJoiningNetwork || (fLastMediaStatus & IFM_ACTIVE) != 0) {
190 				// We already have a link or are already joining.
191 				break;
192 			}
193 
194 			fJoiningNetwork = true;
195 				// TODO: For now we never reset this flag. We can only do that
196 				// after infrastructure has been added to discern a scan reason.
197 				// If we would always auto join we would possibly interfere
198 				// with active scans in the process of connecting to an AP
199 				// either for the initial connection, or after connection loss
200 				// to re-establish the link.
201 
202 			BMessage message(kMsgAutoJoinNetwork);
203 			message.AddString("device", fDevice);
204 			fTarget.SendMessage(&message);
205 			break;
206 		}
207 	}
208 }
209 
210 
211 void
MessageReceived(BMessage * message)212 AutoconfigLooper::MessageReceived(BMessage* message)
213 {
214 	switch (message->what) {
215 		case kMsgReadyToRun:
216 			_ReadyToRun();
217 			break;
218 
219 		case B_NETWORK_MONITOR:
220 			_NetworkMonitorNotification(message);
221 			break;
222 
223 		default:
224 			BLooper::MessageReceived(message);
225 			break;
226 	}
227 }
228 
229