From 3a19a5657bc17fd84e5983669d28ea3ebaaf2b34 Mon Sep 17 00:00:00 2001 From: sr Date: Mon, 15 Jul 2013 19:13:59 +0200 Subject: Rewriiiiiiiiite --- src/server/image.c | 6 + src/server/image.h | 5 + src/server/net.c | 401 +++++++++++++++++++++++----------------------------- src/server/server.c | 9 +- src/server/server.h | 8 -- src/server/uplink.c | 71 ++++++++++ src/server/uplink.h | 14 ++ 7 files changed, 274 insertions(+), 240 deletions(-) create mode 100644 src/server/uplink.c create mode 100644 src/server/uplink.h (limited to 'src') diff --git a/src/server/image.c b/src/server/image.c index 871d3d4..4e69eb2 100644 --- a/src/server/image.c +++ b/src/server/image.c @@ -13,6 +13,12 @@ // ########################################## +dnbd3_image_t *_images[SERVER_MAX_IMAGES]; +int _num_images = 0; +pthread_spinlock_t _images_lock; + +// ########################################## + static void image_load_all(char *base, char *path); static int image_try_load(char *base, char *path); static int image_check_blocks_crc32(int fd, uint32_t *crc32list, int *blocks); diff --git a/src/server/image.h b/src/server/image.h index a27125b..1a64b60 100644 --- a/src/server/image.h +++ b/src/server/image.h @@ -1,6 +1,7 @@ #ifndef _IMAGE_H_ #define _IMAGE_H_ +#include "../config.h" typedef struct { @@ -28,6 +29,10 @@ typedef struct pthread_spinlock_t lock; } dnbd3_image_t; +extern dnbd3_image_t *_images[SERVER_MAX_IMAGES]; +extern int _num_images; +extern pthread_spinlock_t _images_lock; + int image_is_complete(dnbd3_image_t *image); int image_save_cache_map(dnbd3_image_t *image); diff --git a/src/server/net.c b/src/server/net.c index 8dcce3c..5a75a9b 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -31,32 +31,31 @@ #include "sockhelper.h" #include "helper.h" #include "server.h" +#include "image.h" +#include "uplink.h" #include "memlog.h" #include "../serialize.h" #include "../config.h" - static inline char recv_request_header(int sock, dnbd3_request_t *request) { int ret; // Read request header from socket - if ((ret = recv(sock, request, sizeof(*request), MSG_WAITALL)) != sizeof(*request)) - { - if (ret == 0) return 0; - printf("[DEBUG] Error receiving request: Could not read message header (%d/%d)\n", ret, (int)sizeof(*request)); + if ( (ret = recv( sock, request, sizeof(*request), MSG_WAITALL )) != sizeof(*request) ) { + if ( ret == 0 ) return 0; + printf( "[DEBUG] Error receiving request: Could not read message header (%d/%d)\n", ret, (int)sizeof(*request) ); return 0; } // Make sure all bytes are in the right order (endianness) - fixup_request(*request); - if (request->magic != dnbd3_packet_magic) - { - printf("[DEBUG] Magic in client request incorrect (cmd: %d, len: %d)\n", (int)request->cmd, (int)request->size); + fixup_request( *request ); + if ( request->magic != dnbd3_packet_magic ) { + printf( "[DEBUG] Magic in client request incorrect (cmd: %d, len: %d)\n", (int)request->cmd, (int)request->size ); return 0; } // Payload sanity check - if (request->cmd != CMD_GET_BLOCK && request->size > MAX_PAYLOAD) - { - memlogf("[WARNING] Client tries to send a packet of type %d with %d bytes payload. Dropping client.", (int)request->cmd, (int)request->size); + if ( request->cmd != CMD_GET_BLOCK && request->size > MAX_PAYLOAD ) { + memlogf( "[WARNING] Client tries to send a packet of type %d with %d bytes payload. Dropping client.", (int)request->cmd, + (int)request->size ); return 0; } #ifdef _DEBUG @@ -67,48 +66,40 @@ static inline char recv_request_header(int sock, dnbd3_request_t *request) static inline char recv_request_payload(int sock, uint32_t size, serialized_buffer_t *payload) { - if (size == 0) - { - memlogf("[BUG] Called recv_request_payload() to receive 0 bytes"); + if ( size == 0 ) { + memlogf( "[BUG] Called recv_request_payload() to receive 0 bytes" ); return 0; } - if (size > MAX_PAYLOAD) - { - memlogf("[BUG] Called recv_request_payload() for more bytes than the passed buffer could hold!"); + if ( size > MAX_PAYLOAD ) { + memlogf( "[BUG] Called recv_request_payload() for more bytes than the passed buffer could hold!" ); return 0; } - if (recv(sock, payload->buffer, size, MSG_WAITALL) != size) - { - printf("[ERROR] Could not receive request payload of length %d\n", (int)size); + if ( recv( sock, payload->buffer, size, MSG_WAITALL ) != size ) { + printf( "[ERROR] Could not receive request payload of length %d\n", (int)size ); return 0; } // Prepare payload buffer for reading - serializer_reset_read(payload, size); + serializer_reset_read( payload, size ); return 1; } static inline char send_reply(int sock, dnbd3_reply_t *reply, void *payload) { const unsigned int size = reply->size; - fixup_reply(*reply); - if (!payload || size == 0) - { - if (send(sock, reply, sizeof(dnbd3_reply_t), MSG_WAITALL) != sizeof(dnbd3_reply_t)) - { - printf("[DEBUG] Send failed (header-only)\n"); + fixup_reply( *reply ); + if ( !payload || size == 0 ) { + if ( send( sock, reply, sizeof(dnbd3_reply_t), MSG_WAITALL ) != sizeof(dnbd3_reply_t) ) { + printf( "[DEBUG] Send failed (header-only)\n" ); return 0; } - } - else - { + } else { struct iovec iov[2]; iov[0].iov_base = reply; iov[0].iov_len = sizeof(dnbd3_reply_t); iov[1].iov_base = payload; iov[1].iov_len = size; - if (writev(sock, iov, 2) != sizeof(dnbd3_reply_t) + size) - { - printf("[DEBUG] Send failed (reply with payload of %u bytes)\n", size); + if ( writev( sock, iov, 2 ) != sizeof(dnbd3_reply_t) + size ) { + printf( "[DEBUG] Send failed (reply with payload of %u bytes)\n", size ); return 0; } } @@ -117,7 +108,7 @@ static inline char send_reply(int sock, dnbd3_reply_t *reply, void *payload) void *net_client_handler(void *dnbd3_client) { - dnbd3_client_t *client = (dnbd3_client_t *) (uintptr_t) dnbd3_client; + dnbd3_client_t *client = (dnbd3_client_t *)(uintptr_t)dnbd3_client; dnbd3_request_t request; dnbd3_reply_t reply; @@ -145,60 +136,41 @@ void *net_client_handler(void *dnbd3_client) reply.magic = dnbd3_packet_magic; // Receive first packet. This must be CMD_SELECT_IMAGE by protocol specification - if (recv_request_header(client->sock, &request)) - { - if (request.cmd != CMD_SELECT_IMAGE) - { - printf("[DEBUG] Client sent invalid handshake (%d). Dropping Client\n", (int)request.cmd); - } - else - { - if (recv_request_payload(client->sock, request.size, &payload)) - { - client_version = serializer_get_uint16(&payload); - image_name = serializer_get_string(&payload); - rid = serializer_get_uint16(&payload); - client->is_server = serializer_get_uint8(&payload); - if (request.size < 3 || !image_name || client_version < MIN_SUPPORTED_CLIENT) - { - if (client_version < MIN_SUPPORTED_CLIENT) - { - printf("[DEBUG] Client too old\n"); - } - else - { - printf("[DEBUG] Incomplete handshake received\n"); - } - } - else - { - image = image_get(image_name, rid); - const time_t now = time(NULL); - if (image==NULL) - { - printf("[DEBUG] Client requested non-existent image '%s' (rid:%d), rejected\n", image_name, (int)rid); - } - else if (!image->working) - { - printf("[DEBUG] Client requested non-working image '%s' (rid:%d), rejected\n", image_name, (int)rid); + if ( recv_request_header( client->sock, &request ) ) { + if ( request.cmd != CMD_SELECT_IMAGE ) { + printf( "[DEBUG] Client sent invalid handshake (%d). Dropping Client\n", (int)request.cmd ); + } else { + if ( recv_request_payload( client->sock, request.size, &payload ) ) { + client_version = serializer_get_uint16( &payload ); + image_name = serializer_get_string( &payload ); + rid = serializer_get_uint16( &payload ); + client->is_server = serializer_get_uint8( &payload ); + if ( request.size < 3 || !image_name || client_version < MIN_SUPPORTED_CLIENT ) { + if ( client_version < MIN_SUPPORTED_CLIENT ) { + printf( "[DEBUG] Client too old\n" ); + } else { + printf( "[DEBUG] Incomplete handshake received\n" ); } - else - { - image_file = open(image->path, O_RDONLY); - if (image_file >= 0) - { - serializer_reset_write(&payload); - serializer_put_uint16(&payload, PROTOCOL_VERSION); - serializer_put_string(&payload, image->lower_name); - serializer_put_uint16(&payload, image->rid); - serializer_put_uint64(&payload, image->filesize); + } else { + image = image_get( image_name, rid ); + const time_t now = time( NULL ); + if ( image == NULL ) { + printf( "[DEBUG] Client requested non-existent image '%s' (rid:%d), rejected\n", image_name, (int)rid ); + } else if ( !image->working ) { + printf( "[DEBUG] Client requested non-working image '%s' (rid:%d), rejected\n", image_name, (int)rid ); + } else { + image_file = open( image->path, O_RDONLY ); + if ( image_file >= 0 ) { + serializer_reset_write( &payload ); + serializer_put_uint16( &payload, PROTOCOL_VERSION ); + serializer_put_string( &payload, image->lower_name ); + serializer_put_uint16( &payload, image->rid ); + serializer_put_uint64( &payload, image->filesize ); reply.cmd = CMD_SELECT_IMAGE; - reply.size = serializer_get_written_length(&payload); - if (send_reply(client->sock, &reply, &payload)) - { + reply.size = serializer_get_written_length( &payload ); + if ( send_reply( client->sock, &reply, &payload ) ) { client->image = image; - if (!client->is_server) - image->atime = time(NULL); // TODO: check if mutex is needed + if ( !client->is_server ) image->atime = time( NULL ); bOk = TRUE; } @@ -210,38 +182,33 @@ void *net_client_handler(void *dnbd3_client) } // client handling mainloop - if (bOk) - {while (recv_request_header(client->sock, &request)) - { - switch (request.cmd) - { + if ( bOk ) { + while ( recv_request_header( client->sock, &request ) ) { + switch ( request.cmd ) { case CMD_GET_BLOCK: - if (request.offset >= image->filesize) - { + if ( request.offset >= image->filesize ) { // Sanity check - memlogf("[WARNING] Client requested non-existent block"); + memlogf( "[WARNING] Client requested non-existent block" ); reply.size = 0; reply.cmd = CMD_ERROR; - send_reply(client->sock, &reply, NULL); + send_reply( client->sock, &reply, NULL ); break; } - if (request.offset + request.size > image->filesize) - { + if ( request.offset + request.size > image->filesize ) { // Sanity check - memlogf("[WARNING] Client requested data block that extends beyond image size"); + memlogf( "[WARNING] Client requested data block that extends beyond image size" ); reply.size = 0; reply.cmd = CMD_ERROR; - send_reply(client->sock, &reply, NULL); + send_reply( client->sock, &reply, NULL ); break; } - if (request.size > image->filesize) - { + if ( request.size > image->filesize ) { // Sanity check - memlogf("[WARNING] Client requested data block that is bigger than the image size"); + memlogf( "[WARNING] Client requested data block that is bigger than the image size" ); reply.size = 0; reply.cmd = CMD_ERROR; - send_reply(client->sock, &reply, NULL); + send_reply( client->sock, &reply, NULL ); break; } @@ -249,129 +216,118 @@ void *net_client_handler(void *dnbd3_client) reply.size = request.size; reply.handle = request.handle; - fixup_reply(reply); - if (send(client->sock, &reply, sizeof(dnbd3_reply_t), MSG_MORE) != sizeof(dnbd3_reply_t)) - { - printf("[DEBUG] Sending CMD_GET_BLOCK header failed\n"); - return 0; + fixup_reply( reply ); + if ( send( client->sock, &reply, sizeof(dnbd3_reply_t), MSG_MORE ) != sizeof(dnbd3_reply_t) ) { + printf( "[DEBUG] Sending CMD_GET_BLOCK header failed\n" ); + goto exit_client_cleanup; } - if (request.size == 0) // Request for 0 bytes, done after sending header - break; + if ( request.size == 0 ) // Request for 0 bytes, done after sending header + break; - // caching is off - if (image_cache == -1) - { - const ssize_t ret = sendfile(client->sock, image_file, (off_t *)&request.offset, request.size); - if (ret != request.size) - { - printf("[ERROR] sendfile failed (image to net %d/%d)\n", (int)ret, (int)request.size); - close(client->sock); - client->sock = -1; + // no cache map means image is complete + if ( image->cache_map == NULL ) { + const ssize_t ret = sendfile( client->sock, image_file, (off_t *)&request.offset, request.size ); + if ( ret != request.size ) { + printf( "[ERROR] sendfile failed (image to net %d/%d)\n", (int)ret, (int)request.size ); + goto exit_client_cleanup; } break; } - // caching is on - dirty = 0; - todo_size = 0; - todo_offset = request.offset; - cur_offset = request.offset; - last_offset = request.offset + request.size; - - // first make sure the whole requested part is in the local cache file - while(cur_offset < last_offset) - { - map_y = cur_offset >> 15; // div 32768 - map_x = (cur_offset >> 12) & 7; // (X div 4096) mod 8 - bit_mask = 0b00000001 << (map_x); - - cur_offset += 4096; - - if ((image->cache_map[map_y] & bit_mask) != 0) // cache hit - { - if (todo_size != 0) // fetch missing chunks - { - lseek(image_cache, todo_offset, SEEK_SET); - if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) != todo_size) - { - if (image->file == NULL) - printf("[ERROR] Device was closed when local copy was incomplete."); - printf("[ERROR] sendfile failed (copy to cache 1)\n"); - close(client->sock); - client->sock = -1; - // Reset these so we don't update the cache map with false information - dirty = 0; - todo_size = 0; - break; - } - todo_size = 0; - dirty = 1; - } - todo_offset = cur_offset; - } - else - { - todo_size += 4096; - } - } - - // whole request was missing - if (todo_size != 0) - { - lseek(image_cache, todo_offset, SEEK_SET); - if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) != todo_size) - { - printf("[ERROR] sendfile failed (copy to cache 2)\n"); - close(client->sock); - client->sock = -1; - break; - } - dirty = 1; - } - - if (dirty) // cache map needs to be updated as something was missing locally - { - // set 1 in cache map for whole request - cur_offset = request.offset; - while(cur_offset < last_offset) - { - map_y = cur_offset >> 15; - map_x = (cur_offset >> 12) & 7; // mod 8 - bit_mask = 0b00000001 << (map_x); - image->cache_map[map_y] |= bit_mask; - cur_offset += 4096; - } - } - - // send data to client - if (sendfile(client->sock, image_cache, (off_t *) &request.offset, request.size) != request.size) - { - memlogf("[ERROR] sendfile failed (cache to net)\n"); - close(client->sock); - client->sock = -1; - } + printf( "[DEBUG] Caching/Proxying not implemented yet!\n" ); + goto exit_client_cleanup; + + /* + + // caching is on + dirty = 0; + todo_size = 0; + todo_offset = request.offset; + cur_offset = request.offset; + last_offset = request.offset + request.size; + + // first make sure the whole requested part is in the local cache file + while(cur_offset < last_offset) + { + map_y = cur_offset >> 15; // div 32768 + map_x = (cur_offset >> 12) & 7; // (X div 4096) mod 8 + bit_mask = 0b00000001 << (map_x); + + cur_offset += 4096; + + if ((image->cache_map[map_y] & bit_mask) != 0) // cache hit + { + if (todo_size != 0) // fetch missing chunks + { + lseek(image_cache, todo_offset, SEEK_SET); + if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) != todo_size) + { + if (image->file == NULL) + printf("[ERROR] Device was closed when local copy was incomplete."); + printf("[ERROR] sendfile failed (copy to cache 1)\n"); + goto exit_client_cleanup; + } + todo_size = 0; + dirty = 1; + } + todo_offset = cur_offset; + } + else + { + todo_size += 4096; + } + } + + // whole request was missing + if (todo_size != 0) + { + lseek(image_cache, todo_offset, SEEK_SET); + if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) != todo_size) + { + printf("[ERROR] sendfile failed (copy to cache 2)\n"); + goto exit_client_cleanup; + } + dirty = 1; + } + + if (dirty) // cache map needs to be updated as something was missing locally + { + // set 1 in cache map for whole request + cur_offset = request.offset; + while(cur_offset < last_offset) + { + map_y = cur_offset >> 15; + map_x = (cur_offset >> 12) & 7; // mod 8 + bit_mask = 0b00000001 << (map_x); + image->cache_map[map_y] |= bit_mask; + cur_offset += 4096; + } + } + + // send data to client + if (sendfile(client->sock, image_cache, (off_t *) &request.offset, request.size) != request.size) + { + memlogf("[ERROR] sendfile failed (cache to net)\n"); + close(client->sock); + client->sock = -1; + } + */ break; - case CMD_GET_SERVERS: client->is_server = FALSE; // Only clients request list of servers // Build list of known working alt servers - num = 0; - for (i = 0; i < NUMBER_SERVERS; i++) - { - if (image->servers[i].host.type == 0 || image->servers[i].failures > 200) continue; - memcpy(server_list + num++, image->servers + i, sizeof(dnbd3_server_entry_t)); - } + num = uplink_get_matching_alt_servers( &client->host, server_list, NUMBER_SERVERS ); reply.cmd = CMD_GET_SERVERS; reply.size = num * sizeof(dnbd3_server_entry_t); - send_reply(client->sock, &reply, server_list); + send_reply( client->sock, &reply, server_list ); break; case CMD_KEEPALIVE: reply.cmd = CMD_KEEPALIVE; reply.size = 0; - send_reply(client->sock, &reply, NULL); + send_reply( client->sock, &reply, NULL ); break; case CMD_SET_CLIENT_MODE: @@ -379,33 +335,30 @@ void *net_client_handler(void *dnbd3_client) break; default: - memlogf("[ERROR] Unknown command: %d", (int)request.cmd); + memlogf( "[ERROR] Unknown command: %d", (int)request.cmd ); break; } // Check for messages that have been queued from another thread - while (client->sendqueue != NULL) - { + while ( client->sendqueue != NULL ) { dnbd3_binstring_t *message = NULL; - pthread_spin_lock(&_spinlock); - if (client->sendqueue != NULL) - { + pthread_spin_lock( &client->lock ); + if ( client->sendqueue != NULL ) { message = client->sendqueue->data; - client->sendqueue = g_slist_remove(client->sendqueue, message); + client->sendqueue = g_slist_remove( client->sendqueue, message ); } - pthread_spin_unlock(&_spinlock); - send_data(client->sock, message->data, message->len); - free(message); + pthread_spin_unlock( &client->lock ); + send_data( client->sock, message->data, message->len ); + free( message ); } } -} - if (client->sock != -1) - close(client->sock); - if (image_file != -1) close(image_file); - image_release(image); + } + exit_client_cleanup: if ( client->sock != -1 ) close( client->sock ); + if ( image_file != -1 ) close( image_file ); + image_release( image ); client->image = image = NULL; - dnbd3_free_client(client); - pthread_exit((void *) 0); + dnbd3_free_client( client ); + pthread_exit( (void *)0 ); } diff --git a/src/server/server.c b/src/server/server.c index 2c4cf42..7c24358 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -34,6 +34,7 @@ #include "sockhelper.h" #include "server.h" #include "image.h" +#include "uplink.h" #include "net.h" #include "memlog.h" @@ -47,14 +48,6 @@ dnbd3_client_t *_clients[SERVER_MAX_CLIENTS]; int _num_clients = 0; pthread_spinlock_t _clients_lock; -dnbd3_image_t *_images[SERVER_MAX_IMAGES]; -int _num_images = 0; -pthread_spinlock_t _images_lock; - -dnbd3_alt_server_t *_alt_servers[SERVER_MAX_ALTS]; -int _num_alts = 0; -pthread_spinlock_t _alts_lock; - char *_config_file_name = DEFAULT_SERVER_CONFIG_FILE; char *_rpc_password = NULL; char *_cache_dir = NULL; diff --git a/src/server/server.h b/src/server/server.h index b512737..46cd86f 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -70,14 +70,6 @@ extern dnbd3_client_t *_clients[SERVER_MAX_CLIENTS]; extern int _num_clients; extern pthread_spinlock_t _clients_lock; -extern dnbd3_image_t *_images[SERVER_MAX_IMAGES]; -extern int _num_images; -extern pthread_spinlock_t _images_lock; - -extern dnbd3_alt_server_t *_alt_servers[SERVER_MAX_ALTS]; -extern int _num_alts; -extern pthread_spinlock_t _alts_lock; - extern char *_config_file_name, *_rpc_password, *_cache_dir; #ifdef _DEBUG diff --git a/src/server/uplink.c b/src/server/uplink.c new file mode 100644 index 0000000..27e92b3 --- /dev/null +++ b/src/server/uplink.c @@ -0,0 +1,71 @@ +#include "uplink.h" +#include +#include + +dnbd3_alt_server_t *_alt_servers[SERVER_MAX_ALTS]; +int _num_alts = 0; +pthread_spinlock_t _alts_lock; + +/** + * Get known (working) alt servers, ordered by network closeness + * (by finding the smallest possible subnet) + */ +int uplink_get_matching_alt_servers(dnbd3_host_t *host, dnbd3_server_entry_t *output, int size) +{ + if ( host == NULL || host->type == 0 ) return 0; + int i, j; + int count = 0; + int distance[size]; + pthread_spin_lock( &_alts_lock ); + for (i = 0; i < _num_alts; ++i) { + if ( host->type != _alt_servers[i]->host.type ) continue; // Wrong address family + if ( count == 0 ) { + // Trivial - this is the first entry + memcpy( &output[0]->host, &_alt_servers[i]->host, sizeof(dnbd3_host_t) ); + output[0]->failures = 0; + distance[0] = uplink_net_closeness( host, &output[0]->host ); + count++; + } else { + // Other entries already exist, insert in proper position + const int dist = uplink_net_closeness( host, &_alt_servers[i]->host ); + for (j = 0; j < size; ++j) { + if ( j < count && dist <= distance[j] ) continue; + if (j > count) break; // Should never happen but just in case... + if ( j < count ) { + // Check if we're in the middle and need to move other entries... + if (j + 1 < size) { + memmove(&output[j + 1], &output[j], sizeof(dnbd3_server_entry_t) * (size - j - 1)); + memmove(&distance[j + 1], &distance[j], sizeof(int) * (size - j - 1)); + } + } else { + count++; + } + memcpy( &output[j]->host, &_alt_servers[i]->host, sizeof(dnbd3_host_t) ); + output[j]->failures = 0; + distance[j] = dist; + break; + } + } + } + pthread_spin_unlock( &_alts_lock ); + return count; +} + +/** + * Determine how close two addresses are to each other by comparing the number of + * matching bits from the left of the address. Does not count individual bits but + * groups of 4 for speed. + */ +int uplink_net_closeness(dnbd3_host_t *host1, dnbd3_host_t *host2) +{ + if ( host1 == NULL || host2 == NULL || host1->type != host2->type ) return -1; + int retval = 0; + const int max = host1->type == AF_INET ? 4 : 16; + for (int i = 0; i < max; ++i) { + if ( (host1->addr[i] & 0xf0) != (host2->addr[i] & 0xf0) ) return retval; + ++retval; + if ( (host1->addr[i] & 0x0f) != (host2->addr[i] & 0x0f) ) return retval; + ++retval; + } + return retval; +} diff --git a/src/server/uplink.h b/src/server/uplink.h new file mode 100644 index 0000000..77a6ca5 --- /dev/null +++ b/src/server/uplink.h @@ -0,0 +1,14 @@ +#ifndef _UPLINK_H_ +#define _UPLINK_H_ + +#include "../types.h" + +extern dnbd3_alt_server_t *_alt_servers[SERVER_MAX_ALTS]; +extern int _num_alts; +extern pthread_spinlock_t _alts_lock; + +int uplink_get_matching_alt_servers(dnbd3_host_t *host, dnbd3_server_entry_t *output, int size); + +int uplink_net_closeness(dnbd3_host_t *host1, dnbd3_host_t *host2); + +#endif /* UPLINK_H_ */ -- cgit v1.2.3-55-g7522