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