1 /* 2 * Copyright 2006-2009, 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 <net_notifications.h> 21 22 #include "DHCPClient.h" 23 #include "NetServer.h" 24 25 26 static const uint32 kMsgReadyToRun = 'rdyr'; 27 28 29 AutoconfigLooper::AutoconfigLooper(BMessenger target, const char* device) 30 : BLooper(device), 31 fTarget(target), 32 fDevice(device), 33 fCurrentClient(NULL) 34 { 35 BMessage ready(kMsgReadyToRun); 36 PostMessage(&ready); 37 } 38 39 40 AutoconfigLooper::~AutoconfigLooper() 41 { 42 } 43 44 45 void 46 AutoconfigLooper::_RemoveClient() 47 { 48 if (fCurrentClient == NULL) 49 return; 50 51 RemoveHandler(fCurrentClient); 52 delete fCurrentClient; 53 fCurrentClient = NULL; 54 } 55 56 57 void 58 AutoconfigLooper::_Configure() 59 { 60 ifreq request; 61 if (!prepare_request(request, fDevice.String())) 62 return; 63 64 // set IFF_CONFIGURING flag on interface 65 66 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 67 if (socket < 0) 68 return; 69 70 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) { 71 request.ifr_flags |= IFF_CONFIGURING; 72 ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)); 73 } 74 75 // remove current handler 76 77 _RemoveClient(); 78 79 // start with DHCP 80 81 fCurrentClient = new DHCPClient(fTarget, fDevice.String()); 82 AddHandler(fCurrentClient); 83 84 if (fCurrentClient->Initialize() == B_OK) { 85 close(socket); 86 return; 87 } 88 89 _RemoveClient(); 90 91 puts("DHCP failed miserably!"); 92 93 // DHCP obviously didn't work out, take some default values for now 94 // TODO: have a look at zeroconf 95 // TODO: this could also be done add-on based 96 97 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0 98 && (request.ifr_flags & IFF_CONFIGURING) == 0) { 99 // Someone else configured the interface in the mean time 100 close(socket); 101 return; 102 } 103 104 close(socket); 105 106 BMessage interface(kMsgConfigureInterface); 107 interface.AddString("device", fDevice.String()); 108 interface.AddBool("auto", true); 109 110 uint8 mac[6]; 111 uint8 last = 56; 112 if (get_mac_address(fDevice.String(), mac) == B_OK) { 113 // choose IP address depending on the MAC address, if available 114 last = mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]; 115 if (last > 253) 116 last = 253; 117 else if (last == 0) 118 last = 1; 119 } 120 121 // IANA defined the default autoconfig network (for when a DHCP request 122 // fails for some reason) as being 169.254.0.0/255.255.0.0. We are only 123 // generating the last octet but we could also use the 2 last octets if 124 // wanted. 125 char string[64]; 126 snprintf(string, sizeof(string), "169.254.0.%u", last); 127 128 BMessage address; 129 address.AddString("family", "inet"); 130 address.AddString("address", string); 131 interface.AddMessage("address", &address); 132 133 fTarget.SendMessage(&interface); 134 } 135 136 137 void 138 AutoconfigLooper::_ReadyToRun() 139 { 140 start_watching_network(B_WATCH_NETWORK_LINK_CHANGES, this); 141 _Configure(); 142 } 143 144 145 void 146 AutoconfigLooper::MessageReceived(BMessage* message) 147 { 148 switch (message->what) { 149 case kMsgReadyToRun: 150 _ReadyToRun(); 151 break; 152 153 case B_NETWORK_MONITOR: 154 const char* device; 155 int32 opcode; 156 int32 media; 157 if (message->FindInt32("opcode", &opcode) != B_OK 158 || opcode != B_NETWORK_DEVICE_LINK_CHANGED 159 || message->FindString("device", &device) != B_OK 160 || fDevice != device 161 || message->FindInt32("media", &media) != B_OK) 162 break; 163 164 if ((media & IFM_ACTIVE) != 0) { 165 // Reconfigure the interface when we have a link again 166 _Configure(); 167 } 168 break; 169 170 default: 171 BLooper::MessageReceived(message); 172 break; 173 } 174 } 175 176