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 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 44 AutoconfigLooper::~AutoconfigLooper() 45 { 46 } 47 48 49 void 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 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 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 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 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 210 void 211 AutoconfigLooper::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