diff options
Diffstat (limited to 'daemons/maap/common/maap.c')
-rw-r--r-- | daemons/maap/common/maap.c | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/daemons/maap/common/maap.c b/daemons/maap/common/maap.c new file mode 100644 index 00000000..304ee343 --- /dev/null +++ b/daemons/maap/common/maap.c @@ -0,0 +1,1091 @@ +/************************************************************************************* +Copyright (c) 2016-2017, Harman International Industries, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS LISTED BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*************************************************************************************/ + +#include "maap.h" +#include "maap_packet.h" + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#ifdef _WIN32 + /* Windows-specific header values */ +#define random() rand() +#define srandom(s) srand(s) +#else + /* Linux-specific header files */ +#include <linux/if_ether.h> +#endif + +#define MAAP_LOG_COMPONENT "Main" +#include "maap_log.h" + + /* Uncomment the DEBUG_TIMER_MSG define to display timer debug messages. */ +#define DEBUG_TIMER_MSG + + /* Uncomment the DEBUG_NEGOTIATE_MSG define to display negotiation debug messages. */ +#define DEBUG_NEGOTIATE_MSG + + +static int get_count(Maap_Client *mc, Range *range) { + (void)mc; + return range->interval->high - range->interval->low + 1; + } + +static unsigned long long int get_start_address(Maap_Client *mc, Range *range) { + return mc->address_base + range->interval->low; +} + +static unsigned long long int get_end_address(Maap_Client *mc, Range *range) { + return mc->address_base + range->interval->high; +} + +static int send_packet(Maap_Client *mc, MAAP_Packet *p) { + uint8_t *pbuf = NULL; + int ret = 0; + (void)mc; + + pbuf = Net_getPacketBuffer(mc->net); + + pack_maap(p, pbuf); + + ret = Net_queuePacket(mc->net, pbuf); + return ret; +} + +static int send_probe(Maap_Client *mc, Range *range) { + MAAP_Packet p; + + init_packet(&p, mc->dest_mac, mc->src_mac); + + p.message_type = MAAP_PROBE; + p.requested_start_address = get_start_address(mc, range); + p.requested_count = get_count(mc, range); + +#ifdef DEBUG_NEGOTIATE_MSG + if (MAAP_LOG_LEVEL_DEBUG <= MAAP_LOG_LEVEL) { + Time t; + Time_setFromMonotonicTimer(&t); + MAAP_LOGF_DEBUG("Sending probe at %s", Time_dump(&t)); + } +#endif + + return send_packet(mc, &p); +} + +static int send_announce(Maap_Client *mc, Range *range) { + MAAP_Packet p; + + init_packet(&p, mc->dest_mac, mc->src_mac); + + p.message_type = MAAP_ANNOUNCE; + p.requested_start_address = get_start_address(mc, range); + p.requested_count = get_count(mc, range); + +#ifdef DEBUG_NEGOTIATE_MSG + if (MAAP_LOG_LEVEL_DEBUG <= MAAP_LOG_LEVEL) { + Time t; + Time_setFromMonotonicTimer(&t); + MAAP_LOGF_DEBUG("Sending announce at %s", Time_dump(&t)); + } +#endif + + return send_packet(mc, &p); +} + +static int send_defend(Maap_Client *mc, Range *range, uint64_t start, + uint16_t count, uint64_t destination) { + MAAP_Packet p; + uint64_t conflict_start, conflict_end; + + init_packet(&p, mc->dest_mac, mc->src_mac); + + /* Determine the range of addresses where the conflict occurred + * (the union of the requested and allocated ranges). */ + conflict_start = get_start_address(mc, range); + if (conflict_start < start) { conflict_start = start; } + conflict_end = get_end_address(mc, range); + if (conflict_end > start + count - 1) { conflict_end = start + count - 1; } + + p.DA = destination; + p.message_type = MAAP_DEFEND; + p.requested_start_address = start; + p.requested_count = count; + p.conflict_start_address = conflict_start; + p.conflict_count = (uint16_t)(conflict_end - conflict_start + 1); + +#ifdef DEBUG_NEGOTIATE_MSG + if (MAAP_LOG_LEVEL_DEBUG <= MAAP_LOG_LEVEL) { + Time t; + Time_setFromMonotonicTimer(&t); + MAAP_LOGF_DEBUG("Sending defend at %s", Time_dump(&t)); + } +#endif + + return send_packet(mc, &p); +} + +static int inform_initialized(Maap_Client *mc, const void *sender, Maap_Notify_Error result) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_INITIALIZED; + note.id = -1; /* Not used */ + note.start = mc->address_base; + note.count = mc->range_len; + note.result = result; + + add_notify(mc, sender, ¬e); + return 0; +} + +static int inform_acquiring(Maap_Client *mc, Range *range) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_ACQUIRING; + note.id = range->id; + note.start = get_start_address(mc, range); + note.count = get_count(mc, range); + note.result = MAAP_NOTIFY_ERROR_NONE; + + add_notify(mc, range->sender, ¬e); + return 0; +} + +static int inform_acquired(Maap_Client *mc, Range *range, Maap_Notify_Error result) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_ACQUIRED; + note.id = range->id; + note.start = get_start_address(mc, range); + note.count = get_count(mc, range); + note.result = result; + + add_notify(mc, range->sender, ¬e); + return 0; +} + +static int inform_not_acquired(Maap_Client *mc, const void *sender, int id, int range_size, Maap_Notify_Error result) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_ACQUIRED; + note.id = id; + note.start = 0; + note.count = range_size; + note.result = result; + + add_notify(mc, sender, ¬e); + return 0; +} + +static int inform_released(Maap_Client *mc, const void *sender, int id, Range *range, Maap_Notify_Error result) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_RELEASED; + note.id = id; + note.start = (range ? get_start_address(mc, range) : 0); + note.count = (range ? get_count(mc, range) : 0); + note.result = result; + + add_notify(mc, sender, ¬e); + return 0; +} + +static int inform_status(Maap_Client *mc, const void *sender, int id, Range *range, Maap_Notify_Error result) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_STATUS; + note.id = id; + note.start = (range ? get_start_address(mc, range) : 0); + note.count = (range ? get_count(mc, range) : 0); + note.result = result; + + add_notify(mc, sender, ¬e); + return 0; +} + +static int inform_yielded(Maap_Client *mc, const void *sender, int id, Range *range, Maap_Notify_Error result) { + Maap_Notify note; + + note.kind = MAAP_NOTIFY_YIELDED; + note.id = id; + note.start = (range ? get_start_address(mc, range) : 0); + note.count = (range ? get_count(mc, range) : 0 ); + note.result = result; + + add_notify(mc, sender, ¬e); + return 0; +} + +static void start_timer(Maap_Client *mc) { + + if (mc->timer_queue) { + Time_setTimer(mc->timer, &mc->timer_queue->next_act_time); + } +} + +static void remove_range_interval(Interval **root, Interval *node) { + Range *old_range = node->data; + Interval *free_inter, *test_inter; + + /* Remove and free the interval from the set of intervals. + * Note that the interval freed may not be the same one supplied. */ + assert(!old_range || old_range->interval == node); + free_inter = remove_interval(root, node); + assert(free_inter->data == old_range); + free_interval(free_inter); + + /* Make sure the remaining ranges point to the intervals that hold them. + * This is necessary as the Range object may have moved to a different node. */ + for (test_inter = minimum_interval(*root); test_inter != NULL; test_inter = next_interval(test_inter)) { + Range *range = test_inter->data; + assert(range); + assert(range != old_range); + if (range->interval != test_inter) { + range->interval = test_inter; + } + } +} + + +void add_notify(Maap_Client *mc, const void *sender, const Maap_Notify *mn) { + Maap_Notify_List *tmp, *li = calloc(1, sizeof (Maap_Notify_List)); + memcpy(&li->notify, mn, sizeof (Maap_Notify)); + li->sender = sender; + + if (mc->notifies == NULL) { + mc->notifies = li; + } else { + tmp = mc->notifies; + while (tmp->next) { + tmp = tmp->next; + } + tmp->next = li; + } +} + +int get_notify(Maap_Client *mc, const void **sender, Maap_Notify *mn) { + Maap_Notify_List *tmp; + + if (mc->notifies) { + tmp = mc->notifies; + if (mn) { memcpy(mn, &(tmp->notify), sizeof (Maap_Notify)); } + if (sender) { *sender = tmp->sender; } + mc->notifies = tmp->next; + free(tmp); + return 1; + } + return 0; +} + +void print_notify(Maap_Notify *mn, print_notify_callback_t notify_callback, void *callback_data) +{ + char szOutput[300]; + + assert(mn); + + switch (mn->result) + { + case MAAP_NOTIFY_ERROR_NONE: + /* No error. Don't display anything. */ + break; + case MAAP_NOTIFY_ERROR_REQUIRES_INITIALIZATION: + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "MAAP is not initialized, so the command cannot be performed."); + break; + case MAAP_NOTIFY_ERROR_ALREADY_INITIALIZED: + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "MAAP is already initialized, so the values cannot be changed."); + break; + case MAAP_NOTIFY_ERROR_RESERVE_NOT_AVAILABLE: + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "The MAAP reservation is not available, or yield cannot allocate a replacement block. " + "Try again with a smaller address block size."); + break; + case MAAP_NOTIFY_ERROR_RELEASE_INVALID_ID: + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "The MAAP reservation ID is not valid, so cannot be released or report its status."); + break; + case MAAP_NOTIFY_ERROR_OUT_OF_MEMORY: + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "The MAAP application is out of memory."); + break; + case MAAP_NOTIFY_ERROR_INTERNAL: + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "The MAAP application experienced an internal error."); + break; + default: + sprintf(szOutput, "The MAAP application returned an unknown error %d.", mn->result); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + break; + } + + switch (mn->kind) + { + case MAAP_NOTIFY_INITIALIZED: + if (mn->result == MAAP_NOTIFY_ERROR_NONE) { + sprintf(szOutput, "MAAP initialized: 0x%012llx-0x%012llx (Size: %d)", + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + (unsigned int) mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_INFO, szOutput); + } else { + sprintf(szOutput, "MAAP previously initialized to 0x%012llx-0x%012llx (Size: %d)", + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + (unsigned int) mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } + break; + case MAAP_NOTIFY_ACQUIRING: + if (mn->result == MAAP_NOTIFY_ERROR_NONE) { + sprintf(szOutput, "Address range %d querying: 0x%012llx-0x%012llx (Size %d)", + mn->id, + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_INFO, szOutput); + } else { + sprintf(szOutput, "Unknown address range %d acquisition error", mn->id); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } + break; + case MAAP_NOTIFY_ACQUIRED: + if (mn->result == MAAP_NOTIFY_ERROR_NONE) { + sprintf(szOutput, "Address range %d acquired: 0x%012llx-0x%012llx (Size %d)", + mn->id, + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_INFO, szOutput); + } else if (mn->id != -1) { + sprintf(szOutput, "Address range %d of size %d not acquired", + mn->id, mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } else { + sprintf(szOutput, "Address range of size %d not acquired", + mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } + break; + case MAAP_NOTIFY_RELEASED: + if (mn->result == MAAP_NOTIFY_ERROR_NONE) { + sprintf(szOutput, "Address range %d released: 0x%012llx-0x%012llx (Size %d)", + mn->id, + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_INFO, szOutput); + } else { + sprintf(szOutput, "Address range %d not released", + mn->id); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } + break; + case MAAP_NOTIFY_STATUS: + if (mn->result == MAAP_NOTIFY_ERROR_NONE) { + sprintf(szOutput, "ID %d is address range 0x%012llx-0x%012llx (Size %d)", + mn->id, + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_INFO, szOutput); + } else { + sprintf(szOutput, "ID %d is not valid", + mn->id); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } + break; + case MAAP_NOTIFY_YIELDED: + if (mn->result != MAAP_NOTIFY_ERROR_REQUIRES_INITIALIZATION && mn->result != MAAP_NOTIFY_ERROR_RELEASE_INVALID_ID) { + sprintf(szOutput, "Address range %d yielded: 0x%012llx-0x%012llx (Size %d)", + mn->id, + (unsigned long long) mn->start, + (unsigned long long) mn->start + mn->count - 1, + mn->count); + notify_callback(callback_data, MAAP_LOG_LEVEL_WARNING, szOutput); + if (mn->result != MAAP_NOTIFY_ERROR_NONE) { + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, + "A new address range will not be allocated"); + } + } else { + sprintf(szOutput, "ID %d is not valid", + mn->id); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + } + break; + default: + sprintf(szOutput, "Notification type %d not recognized", mn->kind); + notify_callback(callback_data, MAAP_LOG_LEVEL_ERROR, szOutput); + break; + } +} + + +int maap_init_client(Maap_Client *mc, const void *sender, uint64_t range_address_base, uint32_t range_len) { + + if (mc->initialized) { + /* If the desired values are the same as the initialized values, pretend the command succeeded. + * Otherwise, let the sender know the range that was already specified and cannot change. */ + int matches = (range_address_base == mc->address_base && range_len == mc->range_len); + inform_initialized(mc, sender, (matches ? MAAP_NOTIFY_ERROR_NONE : MAAP_NOTIFY_ERROR_ALREADY_INITIALIZED)); + + return (matches ? 0 : -1); + } + + mc->timer = Time_newTimer(); + if (!mc->timer) { + MAAP_LOG_ERROR("Failed to create Timer"); + return -1; + } + + mc->net = Net_newNet(); + if (!mc->net) { + MAAP_LOG_ERROR("Failed to create Net"); + Time_delTimer(mc->timer); + return -1; + } + + mc->address_base = range_address_base; + mc->range_len = range_len; + mc->ranges = NULL; + mc->timer_queue = NULL; + mc->maxid = 0; + mc->notifies = NULL; + + mc->initialized = 1; + + /* Let the sender know the range is now specified. */ + inform_initialized(mc, sender, MAAP_NOTIFY_ERROR_NONE); + + return 0; +} + +void maap_deinit_client(Maap_Client *mc) { + if (mc->initialized) { + while (mc->timer_queue) { + Range * pDel = mc->timer_queue; + mc->timer_queue = mc->timer_queue->next_timer; + if (pDel->state == MAAP_STATE_RELEASED) { free(pDel); } + } + + while (mc->ranges) { + Range *range = mc->ranges->data; + remove_range_interval(&mc->ranges, mc->ranges); + if (range) { free(range); } + } + + if (mc->timer) { + Time_delTimer(mc->timer); + mc->timer = NULL; + } + + if (mc->net) { + Net_delNet(mc->net); + mc->net = NULL; + } + + while (get_notify(mc, NULL, NULL)) { /* Do nothing with the result */ } + + mc->initialized = 0; + } +} + +int rand_ms(int variation) { + /* Return a value between 1 and variation-1, inclusive. + * This is to adhere to IEEE 1722-2016 B.3.4.1 and B.3.4.2. */ + return random() % (variation - 1) + 1; +} + +int schedule_timer(Maap_Client *mc, Range *range) { + Range *rp, *prev_rp; + unsigned long long int ns; + Time ts; + + assert(mc); + assert(range); + +#ifdef DEBUG_TIMER_MSG + if (MAAP_LOG_LEVEL_DEBUG <= MAAP_LOG_LEVEL) { + Time_setFromMonotonicTimer(&ts); + MAAP_LOGF_DEBUG("schedule_timer called at: %s", Time_dump(&ts)); + } +#endif + + if (range->state == MAAP_STATE_PROBING) { + ns = MAAP_PROBE_INTERVAL_BASE + rand_ms(MAAP_PROBE_INTERVAL_VARIATION); + ns = ns * 1000000; +#ifdef DEBUG_TIMER_MSG + MAAP_LOGF_DEBUG("Scheduling probe timer for %llu ns from now", ns); +#endif + Time_setFromNanos(&ts, ns); + Time_setFromMonotonicTimer(&range->next_act_time); + Time_add(&range->next_act_time, &ts); +#ifdef DEBUG_TIMER_MSG + MAAP_LOGF_DEBUG("Expiration time is: %s", Time_dump(&range->next_act_time)); +#endif + } else if (range->state == MAAP_STATE_DEFENDING) { + ns = MAAP_ANNOUNCE_INTERVAL_BASE + rand_ms(MAAP_ANNOUNCE_INTERVAL_VARIATION); + ns = ns * 1000000; +#ifdef DEBUG_TIMER_MSG + MAAP_LOGF_DEBUG("Scheduling defend timer for %llu ns from now", ns); +#endif + Time_setFromNanos(&ts, (uint64_t)ns); + Time_setFromMonotonicTimer(&range->next_act_time); + Time_add(&range->next_act_time, &ts); +#ifdef DEBUG_TIMER_MSG + MAAP_LOGF_DEBUG("Expiration time is: %s", Time_dump(&range->next_act_time)); +#endif + } + + /* Remove the range from the timer queue, if it is already in it. */ + if (mc->timer_queue == range) { + /* Range was at the front of the queue. */ + mc->timer_queue = range->next_timer; + } else if (mc->timer_queue) { + /* Search the rest of the queue. */ + prev_rp = mc->timer_queue; + rp = prev_rp->next_timer; + while (rp && rp != range) { + prev_rp = rp; + rp = rp->next_timer; + } + if (rp) { + /* Range was found. Remove it. */ + prev_rp->next_timer = rp->next_timer; + rp->next_timer = NULL; + } + } + + /* Add the range to the timer queue. */ + if (mc->timer_queue == NULL || + Time_cmp(&range->next_act_time, &mc->timer_queue->next_act_time) < 0) { + range->next_timer = mc->timer_queue; + mc->timer_queue = range; + } else { + rp = mc->timer_queue; + while (rp->next_timer && + Time_cmp(&rp->next_timer->next_act_time, &range->next_act_time) <= 0) + { + rp = rp->next_timer; + } + range->next_timer = rp->next_timer; + rp->next_timer = range; + } + +#ifdef DEBUG_TIMER_MSG + /* Perform a sanity test on the timer queue. */ + { + Range *test = mc->timer_queue; + int i; + for (i = 0; test && i < 100000; ++i) { + assert(test->next_timer != test); + assert(test->next_timer == NULL || Time_cmp(&test->next_act_time, &test->next_timer->next_act_time) <= 0); + test = test->next_timer; + } + if (test) { + MAAP_LOG_ERROR("Timer infinite loop detected!"); + assert(0); + } + } +#endif + + return 0; +} + +static int assign_interval(Maap_Client *mc, Range *range, uint64_t attempt_base, uint16_t len) { + Interval *iv; + int i, rv = INTERVAL_OVERLAP; + uint32_t range_max; + + if (len > mc->range_len) { return -1; } + range_max = mc->range_len - 1; + + /* If we were supplied with a base address to attempt, try that first. */ + if (attempt_base >= mc->address_base && + attempt_base + len - 1 <= mc->address_base + mc->range_len - 1) + { + iv = alloc_interval((uint32_t) (attempt_base - mc->address_base), len); + assert(iv->high <= range_max); + rv = insert_interval(&mc->ranges, iv); + if (rv == INTERVAL_OVERLAP) { + free_interval(iv); + } + } + + /** @todo Use the saved MAAP_ANNOUNCE message ranges to search for addresses likely to be available. + * Old announced ranges (e.g. older than 1.75 minutes) can be deleted if there are no ranges available. */ + + for (i = 0; i < 1000 && rv == INTERVAL_OVERLAP; ++i) { + iv = alloc_interval(random() % (mc->range_len + 1 - len), len); + assert(iv->high <= range_max); + rv = insert_interval(&mc->ranges, iv); + if (rv == INTERVAL_OVERLAP) { + free_interval(iv); + } + } + if (i >= 1000) { + /* There don't appear to be any options! */ + return -1; + } + + iv->data = range; + range->interval = iv; + + return 0; +} + +int maap_reserve_range(Maap_Client *mc, const void *sender, uint64_t attempt_base, uint32_t length) { + int id; + Range *range; + + if (!mc->initialized) { + MAAP_LOG_DEBUG("Reserve not allowed, as MAAP not initialized"); + inform_not_acquired(mc, sender, -1, length, MAAP_NOTIFY_ERROR_REQUIRES_INITIALIZATION); + return -1; + } + + if (length > 0xFFFF || length > mc->range_len) { + /* Range size cannot be more than 16 bits in size, due to the MAAP packet format */ + inform_not_acquired(mc, sender, -1, length, MAAP_NOTIFY_ERROR_RESERVE_NOT_AVAILABLE); + return -1; + } + + range = malloc(sizeof(Range)); + if (range == NULL) { + inform_not_acquired(mc, sender, -1, length, MAAP_NOTIFY_ERROR_OUT_OF_MEMORY); + return -1; + } + + id = ++(mc->maxid); + range->id = id; + range->state = MAAP_STATE_PROBING; + range->counter = MAAP_PROBE_RETRANSMITS; + range->overlapping = 0; + Time_setFromMonotonicTimer(&range->next_act_time); + range->interval = NULL; + range->sender = sender; + range->next_timer = NULL; + + if (assign_interval(mc, range, attempt_base, length) < 0) + { + /* Cannot find any available intervals of the requested size. */ + inform_not_acquired(mc, sender, -1, length, MAAP_NOTIFY_ERROR_RESERVE_NOT_AVAILABLE); + free(range); + return -1; + } + +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOGF_DEBUG("Requested address range, id %d", id); + MAAP_LOGF_DEBUG("Selected address range 0x%012llx-0x%012llx", get_start_address(mc, range), get_end_address(mc, range)); +#endif + inform_acquiring(mc, range); + + schedule_timer(mc, range); + start_timer(mc); + send_probe(mc, range); + + return id; +} + +int maap_release_range(Maap_Client *mc, const void *sender, int id) { + Interval *iv; + Range *range; + + if (!mc->initialized) { + MAAP_LOG_DEBUG("Release not allowed, as MAAP not initialized"); + inform_released(mc, sender, id, NULL, MAAP_NOTIFY_ERROR_REQUIRES_INITIALIZATION); + return -1; + } + + range = mc->timer_queue; + while (range) { + if (range->id == id && range->state != MAAP_STATE_RELEASED) { + inform_released(mc, sender, id, range, MAAP_NOTIFY_ERROR_NONE); + if (sender != range->sender) + { + /* Also inform the sender that originally reserved this range. */ + inform_released(mc, range->sender, id, range, MAAP_NOTIFY_ERROR_NONE); + } + + iv = range->interval; + remove_range_interval(&mc->ranges, iv); + /* memory for range will be freed the next time its timer elapses */ + range->state = MAAP_STATE_RELEASED; + + return 0; + } + range = range->next_timer; + } + + MAAP_LOGF_DEBUG("Range id %d does not exist to release", id); + inform_released(mc, sender, id, NULL, MAAP_NOTIFY_ERROR_RELEASE_INVALID_ID); + return -1; +} + +void maap_range_status(Maap_Client *mc, const void *sender, int id) +{ + Range *range; + + if (!mc->initialized) { + MAAP_LOG_DEBUG("Status not allowed, as MAAP not initialized"); + inform_status(mc, sender, id, NULL, MAAP_NOTIFY_ERROR_REQUIRES_INITIALIZATION); + return; + } + + range = mc->timer_queue; + while (range) { + if (range->id == id && range->state == MAAP_STATE_DEFENDING) { + inform_status(mc, sender, id, range, MAAP_NOTIFY_ERROR_NONE); + return; + } + range = range->next_timer; + } + + MAAP_LOGF_DEBUG("Range id %d does not exist", id); + inform_status(mc, sender, id, NULL, MAAP_NOTIFY_ERROR_RELEASE_INVALID_ID); +} + +int maap_yield_range(Maap_Client *mc, const void *sender, int id) { + Range *range; + MAAP_Packet announce_packet; + uint8_t announce_buffer[MAAP_NET_BUFFER_SIZE]; + + if (!mc->initialized) { + MAAP_LOG_DEBUG("Yield not allowed, as MAAP not initialized"); + inform_yielded(mc, sender, id, NULL, MAAP_NOTIFY_ERROR_REQUIRES_INITIALIZATION); + return -1; + } + + range = mc->timer_queue; + while (range) { + if (range->id == id && range->state == MAAP_STATE_DEFENDING) { + // Create a conflicting packet for this range. + // Use a source address which will always be less than our address, so we should always yield. + init_packet(&announce_packet, 0x010000000000ull, 0x010000000000ull); + announce_packet.message_type = MAAP_ANNOUNCE; + announce_packet.requested_start_address = get_start_address(mc, range); + announce_packet.requested_count = get_count(mc, range); + pack_maap(&announce_packet, announce_buffer); + maap_handle_packet(mc, announce_buffer, MAAP_NET_BUFFER_SIZE); + + return 0; + } + range = range->next_timer; + } + + MAAP_LOGF_DEBUG("Range id %d does not exist", id); + inform_yielded(mc, sender, id, NULL, MAAP_NOTIFY_ERROR_RELEASE_INVALID_ID); + return -1; +} + +int maap_handle_packet(Maap_Client *mc, const uint8_t *stream, int len) { + MAAP_Packet p; + Interval *iv; + uint32_t start; + Range *range; + int rv, num_overlaps; + unsigned long long int own_base, own_max, incoming_base, incoming_max; + + if (len < MAAP_PKT_SIZE) { + MAAP_LOGF_ERROR("Truncated MAAP packet of length %d received, discarding", len); + return -1; + } + rv = unpack_maap(&p, stream); + if (rv != 0) { + MAAP_LOG_ERROR("Error unpacking the MAAP packet"); + return -1; + } + + if (p.Ethertype != MAAP_TYPE || + p.subtype != MAAP_SUBTYPE || + p.control_data_length != 16 ) + { + /* This is not a MAAP packet. Ignore it. */ +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOGF_DEBUG("Ignoring non-MAAP packet of length %d", len); +#endif + return -1; + } + + if (p.version != 0) { + MAAP_LOGF_ERROR("AVTP version %u not supported", p.version); + return -1; + } + + if (p.message_type < MAAP_PROBE || p.message_type > MAAP_ANNOUNCE) { + MAAP_LOGF_ERROR("MAAP packet message type %u not recognized", p.message_type); + return -1; + } + + own_base = mc->address_base; + own_max = mc->address_base + mc->range_len - 1; + incoming_base = p.requested_start_address; + incoming_max = p.requested_start_address + p.requested_count - 1; + +#ifdef DEBUG_NEGOTIATE_MSG + if (p.message_type == MAAP_PROBE) { + MAAP_LOGF_DEBUG("Received PROBE for range 0x%012llx-0x%012llx (Size %u)", incoming_base, incoming_max, p.requested_count); + } + if (p.message_type == MAAP_DEFEND) { + MAAP_LOGF_DEBUG("Received DEFEND for range 0x%012llx-0x%012llx (Size %u),", + incoming_base, incoming_max, p.requested_count); + MAAP_LOGF_DEBUG("conflicting with range 0x%012llx-0x%012llx (Size %u)", + (unsigned long long) p.conflict_start_address, + (unsigned long long) p.conflict_start_address + p.conflict_count - 1, + p.conflict_count); + } + if (p.message_type == MAAP_ANNOUNCE) { + MAAP_LOGF_DEBUG("Received ANNOUNCE for range 0x%012llx-0x%012llx (Size %u)", incoming_base, incoming_max, p.requested_count); + } +#endif + + if (incoming_max < own_base || own_max < incoming_base) { +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOG_DEBUG("Packet refers to a range outside of our concern:"); + MAAP_LOGF_DEBUG(" 0x%012llx < 0x%012llx || 0x%012llx < 0x%012llx", incoming_max, own_base, own_max, incoming_base); +#endif + return 0; + } + + /** @todo If this is a MAAP_ANNOUNCE message, save the announced range and time received for later reference. */ + + /* Flag all the range items that overlap with the incoming packet. */ + num_overlaps = 0; + start = (uint32_t) (p.requested_start_address - mc->address_base); + for (iv = search_interval(mc->ranges, start, p.requested_count); iv != NULL && interval_check_overlap(iv, start, p.requested_count); iv = next_interval(iv)) { + range = iv->data; + range->overlapping = 1; + num_overlaps++; + } + + while (num_overlaps-- > 0) { + /* Find the first item that is still flagged. */ + for (iv = search_interval(mc->ranges, start, p.requested_count); iv != NULL; iv = next_interval(iv)) { + range = iv->data; + if (range->overlapping) { break; } + } + if (!iv) { + /* We reached the end of the list. */ + assert(0); /* We should never get here! */ + break; + } + range->overlapping = 0; + + if (range->state == MAAP_STATE_PROBING) { + + if (p.message_type == MAAP_PROBE && compare_mac_addresses(mc->src_mac, p.SA)) { + /* We won with the lower MAC Address. Do nothing. */ +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOG_DEBUG("Ignoring conflicting probe request"); +#endif + } else { + /* Find an alternate interval, remove old interval, + and restart probe counter */ + int range_size = iv->high - iv->low + 1; + iv->data = NULL; /* Range is moving to a new interval */ + if (assign_interval(mc, range, 0, range_size) < 0) { + /* No interval is available, so stop probing and report an error. */ + MAAP_LOG_WARNING("Unable to find an available address block to probe"); + inform_not_acquired(mc, range->sender, range->id, range_size, MAAP_NOTIFY_ERROR_RESERVE_NOT_AVAILABLE); + remove_range_interval(&mc->ranges, iv); + /* memory will be freed the next time its timer elapses */ + range->state = MAAP_STATE_RELEASED; + } else { +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOGF_DEBUG("Selected new address range 0x%012llx-0x%012llx", + get_start_address(mc, range), get_end_address(mc, range)); +#endif + inform_acquiring(mc, range); + + remove_range_interval(&mc->ranges, iv); + range->counter = MAAP_PROBE_RETRANSMITS; + + schedule_timer(mc, range); + send_probe(mc, range); + } + } + + } else if (range->state == MAAP_STATE_DEFENDING) { + + MAAP_LOGF_INFO("Conflict detected with our range (id %d)!", range->id); +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOGF_DEBUG(" Request of 0x%012llx-0x%012llx conflicts with our range of 0x%012llx-0x%012llx", + incoming_base, incoming_max, + get_start_address(mc, range), get_end_address(mc, range)); +#endif + + if (p.message_type == MAAP_PROBE) { + MAAP_LOG_INFO("DEFEND!"); + send_defend(mc, range, p.requested_start_address, p.requested_count, p.SA); + } else if (compare_mac_addresses(mc->src_mac, p.SA)) { + /* We won with the lower MAC Address. Do nothing. */ + MAAP_LOG_INFO("IGNORE"); + } else { + Range *new_range; + int range_size = iv->high - iv->low + 1; + + MAAP_LOG_INFO("YIELD"); + + /* Start a new reservation request for the owner of the yielded reservation. + * Use the same ID as the yielded range, so the owner can easily track it. + * + * Note: Because our previous range is still in our range list, + * the new range selected will not overlap it. + */ + new_range = malloc(sizeof(Range)); + if (new_range == NULL) { + inform_yielded(mc, range->sender, range->id, range, MAAP_NOTIFY_ERROR_OUT_OF_MEMORY); + } else { + new_range->id = range->id; + new_range->state = MAAP_STATE_PROBING; + new_range->counter = MAAP_PROBE_RETRANSMITS; + new_range->overlapping = 0; + Time_setFromMonotonicTimer(&new_range->next_act_time); + new_range->interval = NULL; + new_range->sender = range->sender; + new_range->next_timer = NULL; + if (assign_interval(mc, new_range, 0, range_size) < 0) + { + /* Cannot find any available intervals of the requested size. */ + inform_yielded(mc, range->sender, range->id, range, MAAP_NOTIFY_ERROR_RESERVE_NOT_AVAILABLE); + free(new_range); + } else { +#ifdef DEBUG_NEGOTIATE_MSG + MAAP_LOGF_DEBUG("Requested replacement address range, id %d", new_range->id); + MAAP_LOGF_DEBUG("Selected replacement address range 0x%012llx-0x%012llx", get_start_address(mc, new_range), get_end_address(mc, new_range)); +#endif + + /* Send a probe for the replacement address range to try. */ + schedule_timer(mc, new_range); + send_probe(mc, new_range); + + inform_yielded(mc, range->sender, range->id, range, MAAP_NOTIFY_ERROR_NONE); + inform_acquiring(mc, new_range); + } + } + + /* We are done with the old range. */ + remove_range_interval(&mc->ranges, iv); + /* memory will be freed the next time its timer elapses */ + range->state = MAAP_STATE_RELEASED; + } + + } + } + + start_timer(mc); + + return 0; +} + +int handle_probe_timer(Maap_Client *mc, Range *range) { + if (range->counter == 0) { + inform_acquired(mc, range, MAAP_NOTIFY_ERROR_NONE); + range->state = MAAP_STATE_DEFENDING; + schedule_timer(mc, range); + send_announce(mc, range); + } else { + range->counter--; + schedule_timer(mc, range); + send_probe(mc, range); + } + + return 0; +} + +int handle_defend_timer(Maap_Client *mc, Range *range) { + schedule_timer(mc, range); + send_announce(mc, range); + + return 0; +} + +int maap_handle_timer(Maap_Client *mc) { + Time currenttime; + Range *range; + + /* Get the current time. */ + Time_setFromMonotonicTimer(¤ttime); +#ifdef DEBUG_TIMER_MSG + MAAP_LOGF_DEBUG("maap_handle_timer called at: %s", Time_dump(¤ttime)); +#endif + + while ((range = mc->timer_queue) && Time_passed(¤ttime, &range->next_act_time)) { +#ifdef DEBUG_TIMER_MSG + MAAP_LOGF_DEBUG("Due timer: %s", Time_dump(&range->next_act_time)); +#endif + mc->timer_queue = range->next_timer; + range->next_timer = NULL; + + if (range->state == MAAP_STATE_PROBING) { +#ifdef DEBUG_TIMER_MSG + MAAP_LOG_DEBUG("Handling probe timer"); +#endif + handle_probe_timer(mc, range); + } else if (range->state == MAAP_STATE_DEFENDING) { +#ifdef DEBUG_TIMER_MSG + MAAP_LOG_DEBUG("Handling defend timer"); +#endif + handle_defend_timer(mc, range); + } else if (range->state == MAAP_STATE_RELEASED) { +#ifdef DEBUG_TIMER_MSG + MAAP_LOG_DEBUG("Freeing released timer"); +#endif + free(range); + } + + } + + start_timer(mc); + + return 0; +} + +int64_t maap_get_delay_to_next_timer(Maap_Client *mc) +{ + long long int timeRemaining; + + if (!(mc->timer) || !(mc->timer_queue)) + { + /* There are no timers waiting, so wait for an hour. + * (No particular reason; it just sounded reasonable.) */ + timeRemaining = 60LL * 60LL * 1000000000LL; + } + else + { + /* Get the time remaining for the next timer. */ + timeRemaining = Time_remaining(mc->timer); + } +#ifdef DEBUG_TIMER_MSG + MAAP_LOG_DEBUG(""); /* Blank line */ + MAAP_LOGF_DEBUG("Time_remaining: %lld ns", timeRemaining); + MAAP_LOG_DEBUG(""); /* Blank line */ +#endif + return timeRemaining; +} |