diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index 2e7d8c23c9..4b78b7bb90 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -29,6 +29,7 @@ typedef struct net_socket { int options; int linger; + int bound_to_device; struct { uint32 buffer_size; diff --git a/src/add-ons/kernel/network/stack/datalink.cpp b/src/add-ons/kernel/network/stack/datalink.cpp index 346b51c4a1..5047d014fc 100644 --- a/src/add-ons/kernel/network/stack/datalink.cpp +++ b/src/add-ons/kernel/network/stack/datalink.cpp @@ -208,19 +208,19 @@ datalink_control_interface(net_domain_private *domain, int32 option, status_t status = (interface == NULL) ? ENODEV : B_OK; switch (option) { - case SIOCGIFINDEX: - if (interface) - request.ifr_index = interface->index; - else - request.ifr_index = 0; - break; + case SIOCGIFINDEX: + if (interface) + request.ifr_index = interface->index; + else + request.ifr_index = 0; + break; - case SIOCGIFNAME: - if (interface) - strlcpy(request.ifr_name, interface->name, IF_NAMESIZE); - else - status = B_BAD_VALUE; // TODO should be ENXIO? - break; + case SIOCGIFNAME: + if (interface) + strlcpy(request.ifr_name, interface->name, IF_NAMESIZE); + else + status = B_BAD_VALUE; // TODO: should be ENXIO? + break; } if (status < B_OK) @@ -386,7 +386,12 @@ datalink_send_datagram(net_protocol *protocol, net_domain *domain, domain = protocol->module->get_domain(protocol); net_route *route = NULL; - status_t status = get_buffer_route(domain, buffer, &route); + status_t status; + if (protocol->socket->bound_to_device > 0) { + status = get_device_route(domain, protocol->socket->bound_to_device, + &route); + } else + status = get_buffer_route(domain, buffer, &route); if (status < B_OK) return status; diff --git a/src/add-ons/kernel/network/stack/interfaces.cpp b/src/add-ons/kernel/network/stack/interfaces.cpp index 9bc0f7d75a..bf46eb918b 100644 --- a/src/add-ons/kernel/network/stack/interfaces.cpp +++ b/src/add-ons/kernel/network/stack/interfaces.cpp @@ -258,6 +258,16 @@ create_interface(net_domain *domain, const char *name, const char *baseName, interface->metric = 0; interface->device_interface = grab_device_interface(deviceInterface); + // setup direct route for bound devices + interface->direct_route.destination = NULL; + interface->direct_route.mask = NULL; + interface->direct_route.gateway = NULL; + interface->direct_route.flags = 0; + interface->direct_route.mtu = 0; + interface->direct_route.interface = interface; + interface->direct_route.ref_count = 1; + // make sure this doesn't get deleted accidently + status_t status = get_domain_datalink_protocols(interface); if (status < B_OK) { delete interface; diff --git a/src/add-ons/kernel/network/stack/interfaces.h b/src/add-ons/kernel/network/stack/interfaces.h index 35faf35b61..65590dabbe 100644 --- a/src/add-ons/kernel/network/stack/interfaces.h +++ b/src/add-ons/kernel/network/stack/interfaces.h @@ -50,6 +50,7 @@ typedef DoublyLinkedList DeviceInterfaceList; struct net_interface_private : net_interface { char base_name[IF_NAMESIZE]; net_device_interface *device_interface; + net_route_private direct_route; }; diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index 3e6b05ebee..537f516cc6 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -183,7 +183,7 @@ process_ancillary_data(net_socket *socket, ancillary_data_container* container, return bytesWritten; dataBuffer += bytesWritten; - dataBufferLen -= bytesWritten; + dataBufferLen -= bytesWritten; } messageHeader->msg_controllen -= dataBufferLen; @@ -235,7 +235,7 @@ socket_open(int family, int type, int protocol, net_socket **_socket) status_t status = create_socket(family, type, protocol, &socket); if (status < B_OK) return status; - + status = socket->first_info->open(socket->first_protocol); if (status < B_OK) { socket_delete(socket); @@ -570,7 +570,7 @@ socket_set_max_backlog(net_socket *_socket, uint32 backlog) mutex_lock(&socket->lock); // first remove the pending connections, then the already connected - // ones as needed + // ones as needed net_socket_private *child; while (socket->child_count > backlog && (child = (net_socket_private *)list_remove_tail_item( @@ -764,7 +764,7 @@ socket_bind(net_socket *socket, const struct sockaddr *address, memcpy(&socket->address, address, sizeof(sockaddr)); - status_t status = socket->first_info->bind(socket->first_protocol, + status_t status = socket->first_info->bind(socket->first_protocol, (sockaddr *)address); if (status < B_OK) { // clear address again, as binding failed @@ -1326,6 +1326,21 @@ socket_set_option(net_socket *socket, int level, int option, const void *value, socket->options &= ~option; return B_OK; + case SO_BINDTODEVICE: + { + if (length != sizeof(int32)) + return B_BAD_VALUE; + + int index = *(const int32 *)value; + if (index < 0) + return B_BAD_VALUE; + + // TODO: we might want to check if the device exists at all + // (although it doesn't really harm when we don't) + socket->bound_to_device = index; + return B_OK; + } + default: break; } @@ -1373,7 +1388,7 @@ socket_socketpair(int family, int type, int protocol, net_socket* sockets[2]) if (error == B_OK) error = socket_listen(sockets[0], 1); - // connect them + // connect them if (error == B_OK) { error = socket_connect(sockets[1], (sockaddr*)&sockets[0]->address, sockets[0]->address.ss_len); diff --git a/src/add-ons/kernel/network/stack/routes.cpp b/src/add-ons/kernel/network/stack/routes.cpp index b9d21c3da3..70570eb933 100644 --- a/src/add-ons/kernel/network/stack/routes.cpp +++ b/src/add-ons/kernel/network/stack/routes.cpp @@ -8,6 +8,7 @@ #include "domains.h" +#include "interfaces.h" #include "routes.h" #include "stack_private.h" #include "utility.h" @@ -607,6 +608,32 @@ get_route(struct net_domain *_domain, const struct sockaddr *address) } +status_t +get_device_route(struct net_domain *_domain, uint32 index, net_route **_route) +{ + net_domain_private *domain = (net_domain_private *)_domain; + + MutexLocker _(domain->lock); + + net_interface_private *interface = NULL; + + while (true) { + interface = (net_interface_private *)list_get_next_item( + &domain->interfaces, interface); + if (interface == NULL) + break; + + if (interface->device->index == index) { + atomic_add(&interface->direct_route.ref_count, 1); + *_route = &interface->direct_route; + return B_OK; + } + } + + return ENETUNREACH; +} + + status_t get_buffer_route(net_domain *_domain, net_buffer *buffer, net_route **_route) { diff --git a/src/add-ons/kernel/network/stack/routes.h b/src/add-ons/kernel/network/stack/routes.h index 7c171cf8d4..c723af9932 100644 --- a/src/add-ons/kernel/network/stack/routes.h +++ b/src/add-ons/kernel/network/stack/routes.h @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -42,8 +42,10 @@ status_t get_route_information(struct net_domain *domain, void *buffer, void invalidate_routes(net_domain *, net_interface *); struct net_route *get_route(struct net_domain *domain, const struct sockaddr *address); +status_t get_device_route(struct net_domain *domain, uint32 index, + struct net_route **_route); status_t get_buffer_route(struct net_domain *domain, - struct net_buffer *buffer, struct net_route **_route); + struct net_buffer *buffer, struct net_route **_route); void put_route(struct net_domain *domain, struct net_route *route); status_t register_route_info(struct net_domain *domain,