diff --git a/src/add-ons/kernel/network/stack/datalink.cpp b/src/add-ons/kernel/network/stack/datalink.cpp index e11f7cc578..0d0f0259cd 100644 --- a/src/add-ons/kernel/network/stack/datalink.cpp +++ b/src/add-ons/kernel/network/stack/datalink.cpp @@ -332,10 +332,7 @@ datalink_control(net_domain *_domain, int32 option, void *value, if (((uint32)request.ifr_flags & IFF_UP) != (interface->flags & IFF_UP)) { if ((interface->flags & IFF_UP) != 0) { - // bring the interface down - interface->flags &= ~(IFF_UP | IFF_LINK); - interface->first_info->interface_down( - interface->first_protocol); + interface_set_down(interface); } else { // bring it up status = interface->first_info->interface_up( @@ -564,6 +561,8 @@ interface_protocol_down(net_datalink_protocol *_protocol) deviceInterface->up_count--; + domain_interface_went_down(protocol->interface); + if (deviceInterface->up_count > 0) return; diff --git a/src/add-ons/kernel/network/stack/domains.cpp b/src/add-ons/kernel/network/stack/domains.cpp index 8781bb4d11..726dd950ac 100644 --- a/src/add-ons/kernel/network/stack/domains.cpp +++ b/src/add-ons/kernel/network/stack/domains.cpp @@ -225,6 +225,49 @@ domain_interfaces_link_changed(net_device *device) } +void +domain_interface_went_down(net_interface *interface) +{ + // the domain should be locked here. always check + // all callers to be sure. We get here via + // interface_set_down(). + + dprintf("domain_interface_went_down(%i, %s)\n", + interface->domain->family, interface->name); + + // domain might have been locked by: + // - domain_removed_device_interface() <--- here + // remove_interface_from_domain() + // delete_interface() + // interface_set_down() + // - datalink_control() <--- here + // interface_set_down() + invalidate_routes(interface->domain, interface); +} + + +void +domain_removed_device_interface(net_device_interface *interface) +{ + BenaphoreLocker locker(sDomainLock); + + net_domain_private *domain = NULL; + while (true) { + domain = (net_domain_private *)list_get_next_item(&sDomains, domain); + if (domain == NULL) + break; + + BenaphoreLocker locker(domain->lock); + + net_interface_private *priv = find_interface(domain, interface->name); + if (priv == NULL) + continue; + + remove_interface_from_domain(priv); + } +} + + status_t register_domain(int family, const char *name, struct net_protocol_module_info *module, diff --git a/src/add-ons/kernel/network/stack/domains.h b/src/add-ons/kernel/network/stack/domains.h index 297cc8f201..6cb837bda8 100644 --- a/src/add-ons/kernel/network/stack/domains.h +++ b/src/add-ons/kernel/network/stack/domains.h @@ -16,6 +16,9 @@ #include +struct net_device_interface; + + struct net_domain_private : net_domain { struct list_link link; @@ -34,6 +37,8 @@ status_t list_domain_interfaces(void *buffer, size_t *_bufferSize); status_t add_interface_to_domain(net_domain *domain, struct ifreq& request); status_t remove_interface_from_domain(net_interface *interface); void domain_interfaces_link_changed(net_device *device); +void domain_interface_went_down(net_interface *); +void domain_removed_device_interface(net_device_interface *); net_domain *get_domain(int family); status_t register_domain(int family, const char *name, diff --git a/src/add-ons/kernel/network/stack/interfaces.cpp b/src/add-ons/kernel/network/stack/interfaces.cpp index afc8933938..06df962839 100644 --- a/src/add-ons/kernel/network/stack/interfaces.cpp +++ b/src/add-ons/kernel/network/stack/interfaces.cpp @@ -63,6 +63,16 @@ domain_receive_adapter(void *cookie, net_buffer *buffer) } +net_device_interface * +grab_device_interface(net_device_interface *interface) +{ + if (interface == NULL || atomic_add(&interface->ref_count, 1) == 0) + return NULL; + + return interface; +} + + // #pragma mark - interfaces @@ -135,7 +145,7 @@ create_interface(net_domain *domain, const char *name, const char *baseName, interface->type = 0; interface->mtu = deviceInterface->device->mtu; interface->metric = 0; - interface->device_interface = deviceInterface; + interface->device_interface = grab_device_interface(deviceInterface); status_t status = get_domain_datalink_protocols(interface); if (status < B_OK) { @@ -153,14 +163,33 @@ create_interface(net_domain *domain, const char *name, const char *baseName, } +void +interface_set_down(net_interface *interface) +{ + if ((interface->flags & IFF_UP) == 0) + return; + + // TODO: IFF_LINK should belong in device only + interface->flags &= ~(IFF_UP | IFF_LINK); + interface->first_info->interface_down(interface->first_protocol); +} + + void delete_interface(net_interface_private *interface) { - if ((interface->flags & IFF_UP) != 0) { - // the interface is still up - we need to change that before deleting it - interface->flags &= ~IFF_UP; - down_device_interface(interface->device_interface); - } + // deleting an interface is fairly complex as we need + // to clear all references to it throughout the stack + + // this will possibly call (if IFF_UP): + // interface_protocol_down() + // domain_interface_went_down() + // invalidate_routes() + // remove_route() + // update_route_infos() + // get_route_internal() + // down_device_interface() -- if upcount reaches 0 + interface_set_down(interface); put_device_interface(interface->device_interface); @@ -388,9 +417,15 @@ down_device_interface(net_device_interface *interface) { net_device *device = interface->device; + dprintf("down_device_interface(%s)\n", interface->name); + device->flags &= ~IFF_UP; interface->module->down(device); + // TODO: there is a race condition between the previous + // ->down and device->module->receive_data which + // locks us here waiting for the reader_thread + // make sure the reader thread is gone before shutting down the interface status_t status; wait_for_thread(interface->reader_thread, &status); @@ -600,7 +635,25 @@ status_t device_removed(net_device *device) { BenaphoreLocker locker(sInterfaceLock); - // TODO: all what this function should do is to clear the IFF_UP flag of the interfaces. + + net_device_interface *interface = find_device_interface(device->name); + if (interface == NULL) + return ENODEV; + + // Propagate the loss of the device throughout the stack. + // This is very complex, refer to delete_interface() for + // further details. + + // this will possibly call: + // remove_interface_from_domain() [domain gets locked] + // delete_interface() + // ... [see delete_interface()] + domain_removed_device_interface(interface); + + // TODO: make sure all readers are gone + // make sure all watchers are gone + // remove device interface + return B_OK; } diff --git a/src/add-ons/kernel/network/stack/interfaces.h b/src/add-ons/kernel/network/stack/interfaces.h index e7571a06e4..19d73c7be9 100644 --- a/src/add-ons/kernel/network/stack/interfaces.h +++ b/src/add-ons/kernel/network/stack/interfaces.h @@ -69,6 +69,7 @@ status_t create_interface(net_domain *domain, const char *name, const char *baseName, net_device_interface *deviceInterface, struct net_interface_private **_interface); void delete_interface(net_interface_private *interface); +void interface_set_down(net_interface *); // device interfaces void get_device_interface_address(net_device_interface *interface, diff --git a/src/add-ons/kernel/network/stack/routes.cpp b/src/add-ons/kernel/network/stack/routes.cpp index 6c65eff9db..e45e226ce8 100644 --- a/src/add-ons/kernel/network/stack/routes.cpp +++ b/src/add-ons/kernel/network/stack/routes.cpp @@ -493,6 +493,28 @@ get_route_information(struct net_domain *_domain, void *value, size_t length) } +void +invalidate_routes(net_domain *_domain, net_interface *interface) +{ + // this function is called with the domain locked + // (see domain_interface_went_down) + net_domain_private *domain = (net_domain_private *)_domain; + + dprintf("invalidate_routes(%i, %s)\n", domain->family, interface->name); + + RouteList::Iterator iterator = domain->routes.GetIterator(); + while (iterator.HasNext()) { + net_route *route = iterator.Next(); + + // TODO handle refcounting, if the route needs to linger + // for some reason we should set interface or + // something of the sorts that invalidates it's reference + if (route->interface == interface) + remove_route(domain, route); + } +} + + struct net_route * get_route(struct net_domain *_domain, const struct sockaddr *address) { diff --git a/src/add-ons/kernel/network/stack/routes.h b/src/add-ons/kernel/network/stack/routes.h index 98f7519830..e41c98e655 100644 --- a/src/add-ons/kernel/network/stack/routes.h +++ b/src/add-ons/kernel/network/stack/routes.h @@ -39,6 +39,7 @@ status_t remove_route(struct net_domain *domain, const struct net_route *route); status_t get_route_information(struct net_domain *domain, void *buffer, size_t length); +void invalidate_routes(net_domain *, net_interface *); struct net_route *get_route(struct net_domain *domain, const struct sockaddr *address); void put_route(struct net_domain *domain, struct net_route *route);