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 "domains.h" 11 #include "interfaces.h" 12 #include "utility.h" 13 #include "stack_private.h" 14 15 #include <net_device.h> 16 17 #include <lock.h> 18 #include <util/AutoLock.h> 19 20 #include <KernelExport.h> 21 22 #include <net/if_media.h> 23 #include <new> 24 #include <string.h> 25 #include <sys/sockio.h> 26 27 28 #define TRACE_DOMAINS 29 #ifdef TRACE_DOMAINS 30 # define TRACE(x) dprintf x 31 #else 32 # define TRACE(x) ; 33 #endif 34 35 #define ENABLE_DEBUGGER_COMMANDS 1 36 37 38 typedef DoublyLinkedList<net_domain_private> DomainList; 39 40 static mutex sDomainLock; 41 static DomainList sDomains; 42 43 44 /*! Scans the domain list for the specified family. 45 You need to hold the sDomainLock when calling this function. 46 */ 47 static net_domain_private* 48 lookup_domain(int family) 49 { 50 DomainList::Iterator iterator = sDomains.GetIterator(); 51 while (net_domain_private* domain = iterator.Next()) { 52 if (domain->family == family) 53 return domain; 54 } 55 56 return NULL; 57 } 58 59 60 #if ENABLE_DEBUGGER_COMMANDS 61 62 63 static int 64 dump_domains(int argc, char** argv) 65 { 66 DomainList::Iterator iterator = sDomains.GetIterator(); 67 while (net_domain_private* domain = iterator.Next()) { 68 kprintf("domain: %p, %s, %d\n", domain, domain->name, domain->family); 69 kprintf(" module: %p\n", domain->module); 70 kprintf(" address_module: %p\n", domain->address_module); 71 72 if (!list_is_empty(&domain->interfaces)) 73 kprintf(" interfaces:\n"); 74 75 net_interface* interface = NULL; 76 while (true) { 77 interface = (net_interface*)list_get_next_item(&domain->interfaces, 78 interface); 79 if (interface == NULL) 80 break; 81 82 kprintf(" %p\n", interface); 83 } 84 85 if (!domain->routes.IsEmpty()) 86 kprintf(" routes:\n"); 87 88 RouteList::Iterator routeIterator = domain->routes.GetIterator(); 89 while (net_route* route = routeIterator.Next()) { 90 kprintf(" %p\n", route); 91 } 92 93 if (!domain->route_infos.IsEmpty()) 94 kprintf(" route infos:\n"); 95 96 RouteInfoList::Iterator infoIterator = domain->route_infos.GetIterator(); 97 while (net_route_info* info = infoIterator.Next()) { 98 kprintf(" %p\n", info); 99 } 100 } 101 102 return 0; 103 } 104 105 106 #endif // ENABLE_DEBUGGER_COMMANDS 107 108 109 // #pragma mark - 110 111 112 /*! Gets the domain of the specified family. 113 */ 114 net_domain* 115 get_domain(int family) 116 { 117 MutexLocker locker(sDomainLock); 118 return lookup_domain(family); 119 } 120 121 122 uint32 123 count_domain_interfaces() 124 { 125 MutexLocker locker(sDomainLock); 126 127 uint32 count = 0; 128 129 DomainList::Iterator iterator = sDomains.GetIterator(); 130 while (net_domain_private* domain = iterator.Next()) { 131 net_interface* interface = NULL; 132 while (true) { 133 interface = (net_interface*)list_get_next_item(&domain->interfaces, 134 interface); 135 if (interface == NULL) 136 break; 137 138 count++; 139 } 140 } 141 142 return count; 143 } 144 145 146 /*! Dumps a list of all interfaces into the supplied userland buffer. 147 If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is 148 returned. 149 */ 150 status_t 151 list_domain_interfaces(void* _buffer, size_t* bufferSize) 152 { 153 MutexLocker locker(sDomainLock); 154 155 UserBuffer buffer(_buffer, *bufferSize); 156 157 DomainList::Iterator iterator = sDomains.GetIterator(); 158 while (net_domain_private* domain = iterator.Next()) { 159 RecursiveLocker locker(domain->lock); 160 161 net_interface* interface = NULL; 162 while (true) { 163 interface = (net_interface*)list_get_next_item(&domain->interfaces, 164 interface); 165 if (interface == NULL) 166 break; 167 168 ifreq request; 169 strlcpy(request.ifr_name, interface->name, IF_NAMESIZE); 170 if (interface->address != NULL) { 171 memcpy(&request.ifr_addr, interface->address, 172 interface->address->sa_len); 173 } else { 174 // empty address 175 request.ifr_addr.sa_len = 2; 176 request.ifr_addr.sa_family = AF_UNSPEC; 177 } 178 179 if (buffer.Copy(&request, IF_NAMESIZE 180 + request.ifr_addr.sa_len) == NULL) 181 return buffer.Status(); 182 } 183 } 184 185 *bufferSize = buffer.ConsumedAmount(); 186 return B_OK; 187 } 188 189 190 status_t 191 add_interface_to_domain(net_domain* _domain, 192 struct ifreq& request) 193 { 194 net_domain_private* domain = (net_domain_private*)_domain; 195 196 const char* deviceName = request.ifr_parameter.device[0] 197 ? request.ifr_parameter.device : request.ifr_name; 198 const char* baseName = request.ifr_parameter.base_name[0] 199 ? request.ifr_parameter.base_name : request.ifr_name; 200 201 net_device_interface* deviceInterface = get_device_interface(deviceName); 202 if (deviceInterface == NULL) 203 return ENODEV; 204 205 RecursiveLocker locker(domain->lock); 206 207 net_interface_private* interface = NULL; 208 status_t status; 209 210 if (find_interface(domain, request.ifr_name) == NULL) { 211 // We must not hold the domain's link when creating the interface: 212 // this will call get_module() which might want to access a network 213 // device when booting from network. 214 locker.Unlock(); 215 status = create_interface(domain, request.ifr_name, 216 baseName, deviceInterface, &interface); 217 locker.Lock(); 218 219 if (find_interface(domain, request.ifr_name) != NULL) { 220 delete_interface(interface); 221 status = B_NAME_IN_USE; 222 } 223 } else 224 status = B_NAME_IN_USE; 225 226 put_device_interface(deviceInterface); 227 228 if (status == B_OK) { 229 list_add_item(&domain->interfaces, interface); 230 notify_interface_added(interface); 231 } 232 233 return status; 234 } 235 236 237 /*! Removes the interface from its domain, and deletes it. 238 You need to hold the domain's lock when calling this function. 239 */ 240 status_t 241 remove_interface_from_domain(net_interface* interface) 242 { 243 net_domain_private* domain = (net_domain_private*)interface->domain; 244 245 list_remove_item(&domain->interfaces, interface); 246 notify_interface_removed(interface); 247 delete_interface((net_interface_private*)interface); 248 return B_OK; 249 } 250 251 252 status_t 253 domain_interface_control(net_domain_private* domain, int32 option, 254 ifreq* request) 255 { 256 const char* name = request->ifr_name; 257 status_t status = B_OK; 258 259 net_device_interface* device = get_device_interface(name, false); 260 if (device == NULL) 261 return ENODEV; 262 263 RecursiveLocker _(domain->lock); 264 265 net_interface* interface = find_interface(domain, name); 266 if (interface != NULL) { 267 switch (option) { 268 case SIOCDIFADDR: 269 remove_interface_from_domain(interface); 270 break; 271 272 case SIOCSIFFLAGS: 273 { 274 uint32 requestFlags = request->ifr_flags; 275 request->ifr_flags &= ~(IFF_UP | IFF_LINK | IFF_BROADCAST); 276 277 if ((requestFlags & IFF_UP) != (interface->flags & IFF_UP)) { 278 if (requestFlags & IFF_UP) { 279 status = interface->first_info->interface_up( 280 interface->first_protocol); 281 if (status == B_OK) 282 interface->flags |= IFF_UP; 283 } else { 284 interface_set_down(interface); 285 } 286 } 287 288 if (status == B_OK) { 289 // TODO: why shouldn't we able to delete IFF_BROADCAST? 290 interface->flags &= IFF_UP | IFF_LINK | IFF_BROADCAST; 291 interface->flags |= request->ifr_flags; 292 } 293 break; 294 } 295 } 296 } 297 298 // If the SIOCDIFADDR call above removed the last interface associated with 299 // the device interface, this will effectively remove the interface 300 put_device_interface(device); 301 302 return status; 303 } 304 305 306 /*! You need to hold the domain lock when calling this function. */ 307 void 308 domain_interface_went_down(net_interface* interface) 309 { 310 ASSERT_LOCKED_RECURSIVE(&((net_domain_private*)interface->domain)->lock); 311 312 TRACE(("domain_interface_went_down(%i, %s)\n", 313 interface->domain->family, interface->name)); 314 315 invalidate_routes(interface->domain, interface); 316 } 317 318 319 void 320 domain_removed_device_interface(net_device_interface* deviceInterface) 321 { 322 MutexLocker locker(sDomainLock); 323 324 DomainList::Iterator iterator = sDomains.GetIterator(); 325 while (net_domain_private* domain = iterator.Next()) { 326 RecursiveLocker locker(domain->lock); 327 328 net_interface_private* interface = find_interface(domain, 329 deviceInterface->device->name); 330 if (interface == NULL) 331 continue; 332 333 remove_interface_from_domain(interface); 334 } 335 } 336 337 338 status_t 339 register_domain(int family, const char* name, 340 struct net_protocol_module_info* module, 341 struct net_address_module_info* addressModule, 342 net_domain** _domain) 343 { 344 TRACE(("register_domain(%d, %s)\n", family, name)); 345 MutexLocker locker(sDomainLock); 346 347 struct net_domain_private* domain = lookup_domain(family); 348 if (domain != NULL) 349 return B_NAME_IN_USE; 350 351 domain = new(std::nothrow) net_domain_private; 352 if (domain == NULL) 353 return B_NO_MEMORY; 354 355 recursive_lock_init(&domain->lock, name); 356 357 domain->family = family; 358 domain->name = name; 359 domain->module = module; 360 domain->address_module = addressModule; 361 362 list_init(&domain->interfaces); 363 364 sDomains.Add(domain); 365 366 *_domain = domain; 367 return B_OK; 368 } 369 370 371 status_t 372 unregister_domain(net_domain* _domain) 373 { 374 TRACE(("unregister_domain(%p, %d, %s)\n", _domain, _domain->family, 375 _domain->name)); 376 377 net_domain_private* domain = (net_domain_private*)_domain; 378 MutexLocker locker(sDomainLock); 379 380 sDomains.Remove(domain); 381 382 net_interface_private* interface = NULL; 383 while (true) { 384 interface = (net_interface_private*)list_remove_head_item( 385 &domain->interfaces); 386 if (interface == NULL) 387 break; 388 389 delete_interface(interface); 390 } 391 392 recursive_lock_destroy(&domain->lock); 393 delete domain; 394 return B_OK; 395 } 396 397 398 status_t 399 init_domains() 400 { 401 mutex_init(&sDomainLock, "net domains"); 402 403 new (&sDomains) DomainList; 404 // static C++ objects are not initialized in the module startup 405 406 #if ENABLE_DEBUGGER_COMMANDS 407 add_debugger_command("net_domains", &dump_domains, 408 "Dump network domains"); 409 #endif 410 return B_OK; 411 } 412 413 414 status_t 415 uninit_domains() 416 { 417 #if ENABLE_DEBUGGER_COMMANDS 418 remove_debugger_command("net_domains", &dump_domains); 419 #endif 420 421 mutex_destroy(&sDomainLock); 422 return B_OK; 423 } 424 425