1 /* 2 * Copyright 2006-2010, 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 */ 8 9 10 #include "AutoconfigLooper.h" 11 12 #include <errno.h> 13 #include <net/if_dl.h> 14 #include <net/if_media.h> 15 #include <net/if_types.h> 16 #include <stdio.h> 17 #include <sys/socket.h> 18 #include <sys/sockio.h> 19 20 #include <NetworkInterface.h> 21 #include <NetworkNotifications.h> 22 23 #include "DHCPClient.h" 24 #include "NetServer.h" 25 26 27 static const uint32 kMsgReadyToRun = 'rdyr'; 28 29 30 AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device) 31 : BLooper(device), 32 fTarget(target), 33 fDevice(device), 34 fCurrentClient(NULL) 35 { 36 memset(fCurrentMac, 0, sizeof(fCurrentMac)); 37 BMessage ready(kMsgReadyToRun); 38 PostMessage(&ready); 39 } 40 41 42 AutoconfigLooper::~AutoconfigLooper() 43 { 44 } 45 46 47 void 48 AutoconfigLooper::_RemoveClient() 49 { 50 if (fCurrentClient == NULL) 51 return; 52 53 RemoveHandler(fCurrentClient); 54 delete fCurrentClient; 55 fCurrentClient = NULL; 56 } 57 58 59 void 60 AutoconfigLooper::_ConfigureIPv4() 61 { 62 // start with DHCP 63 64 if (fCurrentClient == NULL) { 65 fCurrentClient = new DHCPClient(fTarget, fDevice.String()); 66 AddHandler(fCurrentClient); 67 } 68 69 // set IFF_CONFIGURING flag on interface 70 71 BNetworkInterface interface(fDevice.String()); 72 int32 flags = interface.Flags() & ~IFF_AUTO_CONFIGURED; 73 interface.SetFlags(flags | IFF_CONFIGURING); 74 75 if (fCurrentClient->Initialize() == B_OK) 76 return; 77 78 _RemoveClient(); 79 80 puts("DHCP failed miserably!"); 81 82 // DHCP obviously didn't work out, take some default values for now 83 // TODO: have a look at zeroconf 84 // TODO: this could also be done add-on based 85 86 if ((interface.Flags() & IFF_CONFIGURING) == 0) { 87 // Someone else configured the interface in the mean time 88 return; 89 } 90 91 BMessage message(kMsgConfigureInterface); 92 message.AddString("device", fDevice.String()); 93 message.AddBool("auto_configured", true); 94 95 BNetworkAddress link; 96 uint8 last = 56; 97 if (interface.GetHardwareAddress(link) == B_OK) { 98 // choose IP address depending on the MAC address, if available 99 uint8* mac = link.LinkLevelAddress(); 100 last = mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]; 101 if (last > 253) 102 last = 253; 103 else if (last == 0) 104 last = 1; 105 } 106 107 // IANA defined the default autoconfig network (for when a DHCP request 108 // fails for some reason) as being 169.254.0.0/255.255.0.0. We are only 109 // generating the last octet but we could also use the 2 last octets if 110 // wanted. 111 char string[64]; 112 snprintf(string, sizeof(string), "169.254.0.%u", last); 113 114 BMessage address; 115 address.AddInt32("family", AF_INET); 116 address.AddString("address", string); 117 message.AddMessage("address", &address); 118 119 fTarget.SendMessage(&message); 120 } 121 122 123 #ifdef INET6 124 static in6_addr 125 BuildIPv6LinkLocalAddress(uint8 mac[6]) 126 { 127 in6_addr result; 128 129 result.s6_addr[0] = 0xfe; 130 result.s6_addr[1] = 0x80; 131 result.s6_addr[2] = 0; 132 result.s6_addr[3] = 0; 133 result.s6_addr[4] = 0; 134 result.s6_addr[5] = 0; 135 result.s6_addr[6] = 0; 136 result.s6_addr[7] = 0; 137 138 result.s6_addr[8] = mac[0] ^ 0x02; 139 result.s6_addr[9] = mac[1]; 140 result.s6_addr[10] = mac[2]; 141 result.s6_addr[11] = 0xff; 142 result.s6_addr[12] = 0xfe; 143 result.s6_addr[13] = mac[3]; 144 result.s6_addr[14] = mac[4]; 145 result.s6_addr[15] = mac[5]; 146 147 return result; 148 } 149 150 151 void 152 AutoconfigLooper::_ConfigureIPv6LinkLocal(bool add) 153 { 154 // do not touch the loopback device 155 if (!strncmp(fDevice.String(), "loop", 4)) 156 return; 157 158 ifreq request; 159 if (!prepare_request(request, fDevice.String())) 160 return; 161 162 int socket = ::socket(AF_INET6, SOCK_DGRAM, 0); 163 if (socket < 0) 164 return; 165 166 // set IFF_CONFIGURING flag on interface 167 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) { 168 request.ifr_flags |= IFF_CONFIGURING; 169 ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)); 170 } 171 172 uint8 mac[6]; 173 memcpy(mac, fCurrentMac, 6); 174 if (add == true) { 175 if (get_mac_address(fDevice.String(), mac) != B_OK) 176 add = false; 177 } 178 179 if (add == true) { 180 in6_addr inetAddress = BuildIPv6LinkLocalAddress(mac); 181 if (_AddIPv6LinkLocal(socket, inetAddress) != true) 182 add = false; 183 184 // save the MAC address for later usage 185 memcpy(fCurrentMac, mac, 6); 186 } 187 188 if (add == false) { 189 static const uint8 zeroMac[6] = {0}; 190 if (memcmp(fCurrentMac, zeroMac, 6)) { 191 in6_addr inetAddress = BuildIPv6LinkLocalAddress(fCurrentMac); 192 _RemoveIPv6LinkLocal(socket, inetAddress); 193 // reset the stored MAC address 194 memcpy(fCurrentMac, zeroMac, 6); 195 } 196 } 197 198 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0 199 && (request.ifr_flags & IFF_CONFIGURING) == 0) { 200 // Someone else configured the interface in the mean time 201 close(socket); 202 return; 203 } 204 205 close(socket); 206 } 207 208 209 bool 210 AutoconfigLooper::_AddIPv6LinkLocal(int socket, const in6_addr &address) 211 { 212 struct ifreq request; 213 if (!prepare_request(request, fDevice.String())) 214 return false; 215 216 ifaliasreq aliasRequest; 217 memset(&aliasRequest, 0, sizeof(ifaliasreq)); 218 strlcpy(aliasRequest.ifra_name, fDevice.String(), IF_NAMESIZE); 219 aliasRequest.ifra_addr.ss_len = sizeof(sockaddr_in6); 220 aliasRequest.ifra_addr.ss_family = AF_INET6; 221 222 if (ioctl(socket, SIOCAIFADDR, &aliasRequest, sizeof(ifaliasreq)) < 0) { 223 if (errno != B_NAME_IN_USE) 224 return false; 225 } 226 227 sockaddr_in6* socketAddress = (sockaddr_in6*)&request.ifr_addr; 228 socketAddress->sin6_len = sizeof(sockaddr_in6); 229 socketAddress->sin6_family = AF_INET6; 230 231 // address 232 memcpy(&socketAddress->sin6_addr, &address, sizeof(in6_addr)); 233 if (ioctl(socket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0) 234 return false; 235 236 // mask (/64) 237 memset(socketAddress->sin6_addr.s6_addr, 0xff, 8); 238 memset(socketAddress->sin6_addr.s6_addr + 8, 0, 8); 239 240 if (ioctl(socket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0) 241 return false; 242 243 return true; 244 } 245 246 247 void 248 AutoconfigLooper::_RemoveIPv6LinkLocal(int socket, const in6_addr &address) 249 { 250 struct ifreq request; 251 if (!prepare_request(request, fDevice.String())) 252 return; 253 254 sockaddr_in6* socketAddress = (sockaddr_in6*)&request.ifr_addr; 255 socketAddress->sin6_len = sizeof(sockaddr_in6); 256 socketAddress->sin6_family = AF_INET6; 257 258 // address 259 memcpy(&socketAddress->sin6_addr, &address, sizeof(in6_addr)); 260 if (ioctl(socket, SIOCDIFADDR, &request, sizeof(struct ifreq)) < 0) 261 return; 262 } 263 #endif // INET6 264 265 266 void 267 AutoconfigLooper::_ReadyToRun() 268 { 269 start_watching_network(B_WATCH_NETWORK_LINK_CHANGES, this); 270 _ConfigureIPv4(); 271 #ifdef INET6 272 _ConfigureIPv6LinkLocal(true); 273 #endif 274 } 275 276 277 void 278 AutoconfigLooper::MessageReceived(BMessage* message) 279 { 280 switch (message->what) { 281 case kMsgReadyToRun: 282 _ReadyToRun(); 283 break; 284 285 case B_NETWORK_MONITOR: 286 const char* device; 287 int32 opcode; 288 int32 media; 289 if (message->FindInt32("opcode", &opcode) != B_OK 290 || opcode != B_NETWORK_DEVICE_LINK_CHANGED 291 || message->FindString("device", &device) != B_OK 292 || fDevice != device 293 || message->FindInt32("media", &media) != B_OK) 294 break; 295 296 if ((media & IFM_ACTIVE) != 0) { 297 // Reconfigure the interface when we have a link again 298 _ConfigureIPv4(); 299 } 300 #ifdef INET6 301 _ConfigureIPv6LinkLocal((media & IFM_ACTIVE) != 0); 302 #endif 303 break; 304 305 default: 306 BLooper::MessageReceived(message); 307 break; 308 } 309 } 310 311