blob: d80930b37b022101e4d03fd170a235035d2de43e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "router_core_private.h"
#include <stdio.h>
static void qdr_add_router_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_del_router_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_set_link_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_remove_link_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_set_next_hop_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_remove_next_hop_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_set_cost_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_set_valid_origins_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_map_destination_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_unmap_destination_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_subscribe_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_unsubscribe_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
//==================================================================================
// Interface Functions
//==================================================================================
void qdr_core_add_router(qdr_core_t *core, const char *address, int router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_add_router_CT, "add_router");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.address = qdr_field(address);
qdr_action_enqueue(core, action);
}
void qdr_core_del_router(qdr_core_t *core, int router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_del_router_CT, "del_router");
action->args.route_table.router_maskbit = router_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_set_link(qdr_core_t *core, int router_maskbit, int link_maskbit)
{
qdr_action_t *action = qdr_action(qdr_set_link_CT, "set_link");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.link_maskbit = link_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_remove_link(qdr_core_t *core, int router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_remove_link_CT, "remove_link");
action->args.route_table.router_maskbit = router_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_set_next_hop(qdr_core_t *core, int router_maskbit, int nh_router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_set_next_hop_CT, "set_next_hop");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.nh_router_maskbit = nh_router_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_remove_next_hop(qdr_core_t *core, int router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_remove_next_hop_CT, "remove_next_hop");
action->args.route_table.router_maskbit = router_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_set_cost(qdr_core_t *core, int router_maskbit, int cost)
{
qdr_action_t *action = qdr_action(qdr_set_cost_CT, "set_cost");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.cost = cost;
qdr_action_enqueue(core, action);
}
void qdr_core_set_valid_origins(qdr_core_t *core, int router_maskbit, qd_bitmask_t *routers)
{
qdr_action_t *action = qdr_action(qdr_set_valid_origins_CT, "set_valid_origins");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.router_set = routers;
qdr_action_enqueue(core, action);
}
void qdr_core_map_destination(qdr_core_t *core, int router_maskbit, const char *address_hash)
{
qdr_action_t *action = qdr_action(qdr_map_destination_CT, "map_destination");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.address = qdr_field(address_hash);
qdr_action_enqueue(core, action);
}
void qdr_core_unmap_destination(qdr_core_t *core, int router_maskbit, const char *address_hash)
{
qdr_action_t *action = qdr_action(qdr_unmap_destination_CT, "unmap_destination");
action->args.route_table.router_maskbit = router_maskbit;
action->args.route_table.address = qdr_field(address_hash);
qdr_action_enqueue(core, action);
}
void qdr_core_route_table_handlers(qdr_core_t *core,
void *context,
qdr_mobile_added_t mobile_added,
qdr_mobile_removed_t mobile_removed,
qdr_link_lost_t link_lost)
{
core->rt_context = context;
core->rt_mobile_added = mobile_added;
core->rt_mobile_removed = mobile_removed;
core->rt_link_lost = link_lost;
}
qdr_subscription_t *qdr_core_subscribe(qdr_core_t *core,
const char *address,
char aclass,
char phase,
qd_address_treatment_t treatment,
qdr_receive_t on_message,
void *context)
{
qdr_subscription_t *sub = NEW(qdr_subscription_t);
sub->core = core;
sub->addr = 0;
sub->on_message = on_message;
sub->on_message_context = context;
qdr_action_t *action = qdr_action(qdr_subscribe_CT, "subscribe");
action->args.io.address = qdr_field(address);
action->args.io.address_class = aclass;
action->args.io.address_phase = phase;
action->args.io.subscription = sub;
action->args.io.treatment = treatment;
qdr_action_enqueue(core, action);
return sub;
}
void qdr_core_unsubscribe(qdr_subscription_t *sub)
{
if (sub) {
qdr_action_t *action = qdr_action(qdr_unsubscribe_CT, "unsubscribe");
action->args.io.subscription = sub;
qdr_action_enqueue(sub->core, action);
}
}
//==================================================================================
// In-Thread Functions
//==================================================================================
//
// React to the updated cost of a router node. The core->routers list is to be kept
// sorted by cost, from least to most.
//
void qdr_route_table_update_cost_CT(qdr_core_t *core, qdr_node_t *rnode)
{
qdr_node_t *ptr;
bool needs_reinsertion = false;
ptr = DEQ_PREV(rnode);
if (ptr && ptr->cost > rnode->cost)
needs_reinsertion = true;
else {
ptr = DEQ_NEXT(rnode);
if (ptr && ptr->cost < rnode->cost)
needs_reinsertion = true;
}
if (needs_reinsertion) {
core->cost_epoch++;
DEQ_REMOVE(core->routers, rnode);
ptr = DEQ_TAIL(core->routers);
while (ptr) {
if (rnode->cost >= ptr->cost) {
DEQ_INSERT_AFTER(core->routers, rnode, ptr);
break;
}
ptr = DEQ_PREV(ptr);
}
if (!ptr)
DEQ_INSERT_HEAD(core->routers, rnode);
}
}
void qdr_route_table_setup_CT(qdr_core_t *core)
{
DEQ_INIT(core->addrs);
DEQ_INIT(core->routers);
core->addr_hash = qd_hash(12, 32, 0);
core->conn_id_hash = qd_hash(6, 4, 0);
core->cost_epoch = 1;
if (core->router_mode == QD_ROUTER_MODE_INTERIOR) {
core->hello_addr = qdr_add_local_address_CT(core, 'L', "qdhello", QD_TREATMENT_MULTICAST_FLOOD);
core->router_addr_L = qdr_add_local_address_CT(core, 'L', "qdrouter", QD_TREATMENT_MULTICAST_FLOOD);
core->routerma_addr_L = qdr_add_local_address_CT(core, 'L', "qdrouter.ma", QD_TREATMENT_MULTICAST_ONCE);
core->router_addr_T = qdr_add_local_address_CT(core, 'T', "qdrouter", QD_TREATMENT_MULTICAST_FLOOD);
core->routerma_addr_T = qdr_add_local_address_CT(core, 'T', "qdrouter.ma", QD_TREATMENT_MULTICAST_ONCE);
core->neighbor_free_mask = qd_bitmask(1);
core->routers_by_mask_bit = NEW_PTR_ARRAY(qdr_node_t, qd_bitmask_width());
core->control_links_by_mask_bit = NEW_PTR_ARRAY(qdr_link_t, qd_bitmask_width());
core->data_links_by_mask_bit = NEW_PTR_ARRAY(qdr_link_t, qd_bitmask_width());
for (int idx = 0; idx < qd_bitmask_width(); idx++) {
core->routers_by_mask_bit[idx] = 0;
core->control_links_by_mask_bit[idx] = 0;
core->data_links_by_mask_bit[idx] = 0;
}
}
}
static void qdr_add_router_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
qdr_field_t *address = action->args.route_table.address;
if (discard) {
qdr_field_free(address);
return;
}
do {
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "add_router: Router maskbit out of range: %d", router_maskbit);
break;
}
if (core->routers_by_mask_bit[router_maskbit] != 0) {
qd_log(core->log, QD_LOG_CRITICAL, "add_router: Router maskbit already in use: %d", router_maskbit);
break;
}
//
// Hash lookup the address to ensure there isn't an existing router address.
//
qd_field_iterator_t *iter = address->iterator;
qdr_address_t *addr;
qd_address_iterator_reset_view(iter, ITER_VIEW_ADDRESS_HASH);
qd_hash_retrieve(core->addr_hash, iter, (void**) &addr);
if (addr) {
qd_log(core->log, QD_LOG_CRITICAL, "add_router: Data inconsistency for router-maskbit %d", router_maskbit);
assert(addr == 0); // Crash in debug mode. This should never happen
break;
}
//
// Create an address record for this router and insert it in the hash table.
// This record will be found whenever a "foreign" topological address to this
// remote router is looked up.
//
addr = qdr_address_CT(core, QD_TREATMENT_ANYCAST_CLOSEST);
qd_hash_insert(core->addr_hash, iter, addr, &addr->hash_handle);
DEQ_INSERT_TAIL(core->addrs, addr);
//
// Create a router-node record to represent the remote router.
//
qdr_node_t *rnode = new_qdr_node_t();
DEQ_ITEM_INIT(rnode);
rnode->owning_addr = addr;
rnode->mask_bit = router_maskbit;
rnode->next_hop = 0;
rnode->peer_control_link = 0;
rnode->peer_data_link = 0;
rnode->ref_count = 0;
rnode->valid_origins = qd_bitmask(0);
rnode->cost = 0;
//
// Insert at the head of the list because we don't yet know the cost to this
// router node and we've set the cost to zero. This puts it in a properly-sorted
// position. Also, don't bump the cost_epoch here because this new router won't be
// used until it is assigned a cost.
//
DEQ_INSERT_HEAD(core->routers, rnode);
//
// Link the router record to the address record.
//
qd_bitmask_set_bit(addr->rnodes, router_maskbit);
//
// Link the router record to the router address records.
// Use the T-class addresses only.
//
qd_bitmask_set_bit(core->router_addr_T->rnodes, router_maskbit);
qd_bitmask_set_bit(core->routerma_addr_T->rnodes, router_maskbit);
//
// Bump the ref-count by three for each of the above links.
//
rnode->ref_count += 3;
//
// Add the router record to the mask-bit index.
//
core->routers_by_mask_bit[router_maskbit] = rnode;
} while (false);
qdr_field_free(address);
}
static void qdr_del_router_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "del_router: Router maskbit out of range: %d", router_maskbit);
return;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "del_router: Deleting nonexistent router: %d", router_maskbit);
return;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
qdr_address_t *oaddr = rnode->owning_addr;
assert(oaddr);
//
// Unlink the router node from the address record
//
qd_bitmask_clear_bit(oaddr->rnodes, router_maskbit);
qd_bitmask_clear_bit(core->router_addr_T->rnodes, router_maskbit);
qd_bitmask_clear_bit(core->routerma_addr_T->rnodes, router_maskbit);
rnode->ref_count -= 3;
//
// While the router node has a non-zero reference count, look for addresses
// to unlink the node from.
//
qdr_address_t *addr = DEQ_HEAD(core->addrs);
while (addr && rnode->ref_count > 0) {
if (qd_bitmask_clear_bit(addr->rnodes, router_maskbit))
//
// If the cleared bit was originally set, decrement the ref count
//
rnode->ref_count--;
addr = DEQ_NEXT(addr);
}
assert(rnode->ref_count == 0);
//
// Free the router node and the owning address records.
//
qd_bitmask_free(rnode->valid_origins);
DEQ_REMOVE(core->routers, rnode);
core->cost_epoch++;
free_qdr_node_t(rnode);
qd_hash_remove_by_handle(core->addr_hash, oaddr->hash_handle);
DEQ_REMOVE(core->addrs, oaddr);
qd_hash_handle_free(oaddr->hash_handle);
core->routers_by_mask_bit[router_maskbit] = 0;
qd_bitmask_free(oaddr->rnodes);
free_qdr_address_t(oaddr);
}
static void qdr_set_link_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
int link_maskbit = action->args.route_table.link_maskbit;
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_link: Router maskbit out of range: %d", router_maskbit);
return;
}
if (link_maskbit >= qd_bitmask_width() || link_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_link: Link maskbit out of range: %d", link_maskbit);
return;
}
if (core->control_links_by_mask_bit[link_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_link: Invalid link reference: %d", link_maskbit);
return;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_link: Router not found");
return;
}
//
// Add the peer_link reference to the router record.
//
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
rnode->peer_control_link = core->control_links_by_mask_bit[link_maskbit];
rnode->peer_data_link = core->data_links_by_mask_bit[link_maskbit];
}
static void qdr_remove_link_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "remove_link: Router maskbit out of range: %d", router_maskbit);
return;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "remove_link: Router not found");
return;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
rnode->peer_control_link = 0;
rnode->peer_data_link = 0;
}
static void qdr_set_next_hop_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
int nh_router_maskbit = action->args.route_table.nh_router_maskbit;
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_next_hop: Router maskbit out of range: %d", router_maskbit);
return;
}
if (nh_router_maskbit >= qd_bitmask_width() || nh_router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_next_hop: Next hop router maskbit out of range: %d", router_maskbit);
return;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_next_hop: Router not found");
return;
}
if (core->routers_by_mask_bit[nh_router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_next_hop: Next hop router not found");
return;
}
if (router_maskbit != nh_router_maskbit) {
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
rnode->next_hop = core->routers_by_mask_bit[nh_router_maskbit];
}
}
static void qdr_remove_next_hop_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "remove_next_hop: Router maskbit out of range: %d", router_maskbit);
return;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
rnode->next_hop = 0;
}
static void qdr_set_cost_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
int cost = action->args.route_table.cost;
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_cost: Router maskbit out of range: %d", router_maskbit);
return;
}
if (cost < 1) {
qd_log(core->log, QD_LOG_CRITICAL, "set_cost: Invalid cost %d for maskbit: %d", cost, router_maskbit);
return;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
rnode->cost = cost;
qdr_route_table_update_cost_CT(core, rnode);
}
static void qdr_set_valid_origins_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
qd_bitmask_t *valid_origins = action->args.route_table.router_set;
if (discard) {
qd_bitmask_free(valid_origins);
return;
}
do {
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_valid_origins: Router maskbit out of range: %d", router_maskbit);
break;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "set_valid_origins: Router not found");
break;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
if (rnode->valid_origins)
qd_bitmask_free(rnode->valid_origins);
rnode->valid_origins = valid_origins;
valid_origins = 0;
} while (false);
if (valid_origins)
qd_bitmask_free(valid_origins);
}
static void qdr_map_destination_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
qdr_field_t *address = action->args.route_table.address;
if (discard) {
qdr_field_free(address);
return;
}
do {
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "map_destination: Router maskbit out of range: %d", router_maskbit);
break;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "map_destination: Router not found");
break;
}
qd_field_iterator_t *iter = address->iterator;
qdr_address_t *addr = 0;
qd_hash_retrieve(core->addr_hash, iter, (void**) &addr);
if (!addr) {
addr = qdr_address_CT(core, qdr_treatment_for_address_hash_CT(core, iter));
qd_hash_insert(core->addr_hash, iter, addr, &addr->hash_handle);
DEQ_ITEM_INIT(addr);
DEQ_INSERT_TAIL(core->addrs, addr);
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
qd_bitmask_set_bit(addr->rnodes, router_maskbit);
rnode->ref_count++;
addr->cost_epoch--;
qdr_addr_start_inlinks_CT(core, addr);
} while (false);
qdr_field_free(address);
}
static void qdr_unmap_destination_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
int router_maskbit = action->args.route_table.router_maskbit;
qdr_field_t *address = action->args.route_table.address;
if (discard) {
qdr_field_free(address);
return;
}
do {
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "unmap_destination: Router maskbit out of range: %d", router_maskbit);
break;
}
if (core->routers_by_mask_bit[router_maskbit] == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "unmap_destination: Router not found");
break;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
qd_field_iterator_t *iter = address->iterator;
qdr_address_t *addr = 0;
qd_hash_retrieve(core->addr_hash, iter, (void**) &addr);
if (!addr) {
qd_log(core->log, QD_LOG_CRITICAL, "unmap_destination: Address not found");
break;
}
qd_bitmask_clear_bit(addr->rnodes, router_maskbit);
rnode->ref_count--;
addr->cost_epoch--;
//
// TODO - If this affects a waypoint, create the proper side effects
//
qdr_check_addr_CT(core, addr, false);
} while (false);
qdr_field_free(address);
}
static void qdr_subscribe_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
qdr_field_t *address = action->args.io.address;
qdr_subscription_t *sub = action->args.io.subscription;
if (!discard) {
char aclass = action->args.io.address_class;
char phase = action->args.io.address_phase;
qdr_address_t *addr = 0;
char *astring = (char*) qd_field_iterator_copy(address->iterator);
qd_log(core->log, QD_LOG_INFO, "In-process subscription %c/%s", aclass, astring);
free(astring);
qd_address_iterator_override_prefix(address->iterator, aclass);
if (aclass == 'M')
qd_address_iterator_set_phase(address->iterator, phase);
qd_address_iterator_reset_view(address->iterator, ITER_VIEW_ADDRESS_HASH);
qd_hash_retrieve(core->addr_hash, address->iterator, (void**) &addr);
if (!addr) {
addr = qdr_address_CT(core, action->args.io.treatment);
qd_hash_insert(core->addr_hash, address->iterator, addr, &addr->hash_handle);
DEQ_ITEM_INIT(addr);
DEQ_INSERT_TAIL(core->addrs, addr);
}
sub->addr = addr;
DEQ_ITEM_INIT(sub);
DEQ_INSERT_TAIL(addr->subscriptions, sub);
qdr_addr_start_inlinks_CT(core, addr);
} else
free(sub);
qdr_field_free(address);
}
static void qdr_unsubscribe_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
qdr_subscription_t *sub = action->args.io.subscription;
if (!discard) {
DEQ_REMOVE(sub->addr->subscriptions, sub);
sub->addr = 0;
qdr_check_addr_CT(sub->core, sub->addr, false);
}
free(sub);
}
//==================================================================================
// Call-back Functions
//==================================================================================
static void qdr_do_mobile_added(qdr_core_t *core, qdr_general_work_t *work)
{
char *address_hash = qdr_field_copy(work->field);
if (address_hash) {
core->rt_mobile_added(core->rt_context, address_hash);
free(address_hash);
}
qdr_field_free(work->field);
}
static void qdr_do_mobile_removed(qdr_core_t *core, qdr_general_work_t *work)
{
char *address_hash = qdr_field_copy(work->field);
if (address_hash) {
core->rt_mobile_removed(core->rt_context, address_hash);
free(address_hash);
}
qdr_field_free(work->field);
}
static void qdr_do_link_lost(qdr_core_t *core, qdr_general_work_t *work)
{
core->rt_link_lost(core->rt_context, work->maskbit);
}
void qdr_post_mobile_added_CT(qdr_core_t *core, const char *address_hash)
{
qdr_general_work_t *work = qdr_general_work(qdr_do_mobile_added);
work->field = qdr_field(address_hash);
qdr_post_general_work_CT(core, work);
}
void qdr_post_mobile_removed_CT(qdr_core_t *core, const char *address_hash)
{
qdr_general_work_t *work = qdr_general_work(qdr_do_mobile_removed);
work->field = qdr_field(address_hash);
qdr_post_general_work_CT(core, work);
}
void qdr_post_link_lost_CT(qdr_core_t *core, int link_maskbit)
{
qdr_general_work_t *work = qdr_general_work(qdr_do_link_lost);
work->maskbit = link_maskbit;
qdr_post_general_work_CT(core, work);
}