blob: d6fdb153bf9fe0b0f023f4f11f60b32d96afa15e [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 "route_control.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_flush_destinations_CT (qdr_core_t *core, qdr_action_t *action, bool discard);
static void qdr_mobile_seq_advanced_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_flush_destinations(qdr_core_t *core, int router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_flush_destinations_CT, "flush_destinations");
action->args.route_table.router_maskbit = router_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_mobile_seq_advanced(qdr_core_t *core, int router_maskbit)
{
qdr_action_t *action = qdr_action(qdr_mobile_seq_advanced_CT, "mobile_seq_advanced");
action->args.route_table.router_maskbit = router_maskbit;
qdr_action_enqueue(core, action);
}
void qdr_core_route_table_handlers(qdr_core_t *core,
void *context,
qdr_set_mobile_seq_t set_mobile_seq,
qdr_set_my_mobile_seq_t set_my_mobile_seq,
qdr_link_lost_t link_lost)
{
core->rt_context = context;
core->rt_set_mobile_seq = set_mobile_seq;
core->rt_set_my_mobile_seq = set_my_mobile_seq;
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,
bool in_core,
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;
sub->in_core = in_core;
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;
core->addr_parse_tree = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
core->link_route_tree[QD_INCOMING] = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
core->link_route_tree[QD_OUTGOING] = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
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->hello_addr->router_control_only = true;
core->router_addr_L->router_control_only = true;
core->routerma_addr_L->router_control_only = true;
core->router_addr_T->router_control_only = true;
core->routerma_addr_T->router_control_only = true;
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_ARRAY(qdr_priority_sheaf_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].count = 0;
for (int priority = 0; priority < QDR_N_PRIORITIES; ++ priority)
core->data_links_by_mask_bit[idx].links[priority] = 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_iterator_t *iter = address->iterator;
qdr_address_t *addr;
qd_iterator_reset_view(iter, ITER_VIEW_ADDRESS_HASH);
qd_hash_retrieve(core->addr_hash, iter, (void**) &addr);
if (!addr) {
//
// 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, 0);
qd_hash_insert(core->addr_hash, iter, addr, &addr->hash_handle);
DEQ_INSERT_TAIL(core->addrs, addr);
}
//
// Bump the address's ref_count for the time that it is associated with an existing remote router node.
//
addr->ref_count++;
//
// Create a router-node record to represent the remote router.
//
qdr_node_t *rnode = new_qdr_node_t();
ZERO(rnode);
rnode->owning_addr = addr;
rnode->mask_bit = router_maskbit;
rnode->link_mask_bit = -1;
rnode->valid_origins = qd_bitmask(0);
qd_iterator_reset_view(iter, ITER_VIEW_ALL);
int addr_len = qd_iterator_length(iter);
rnode->wire_address_ma = (char*) malloc(addr_len + 4);
qd_iterator_ncopy(iter, (unsigned char*) rnode->wire_address_ma, addr_len);
strcpy(rnode->wire_address_ma + addr_len, ".ma");
//
// 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.
//
qdr_router_node_free(core, rnode);
//
// Check the address and free it if there are no other interested parties tracking it
//
oaddr->ref_count--;
qdr_check_addr_CT(core, 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->link_mask_bit = link_maskbit;
qdr_addr_start_inlinks_CT(core, rnode->owning_addr);
}
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->link_mask_bit = -1;
}
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];
qdr_addr_start_inlinks_CT(core, rnode->owning_addr);
}
}
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_flush_destinations_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
if (!!discard)
return;
int router_maskbit = action->args.route_table.router_maskbit;
do {
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "flush_destinations: Router maskbit out of range: %d", router_maskbit);
break;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
if (rnode == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "flush_destinations: Router not found");
break;
}
//
// Raise the event to be picked up by core modules.
//
qdrc_event_router_raise(core, QDRC_EVENT_ROUTER_MOBILE_FLUSH, rnode);
} while (false);
}
static void qdr_mobile_seq_advanced_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
{
if (!!discard)
return;
int router_maskbit = action->args.route_table.router_maskbit;
do {
if (router_maskbit >= qd_bitmask_width() || router_maskbit < 0) {
qd_log(core->log, QD_LOG_CRITICAL, "seq_advanced: Router maskbit out of range: %d", router_maskbit);
break;
}
qdr_node_t *rnode = core->routers_by_mask_bit[router_maskbit];
if (rnode == 0) {
qd_log(core->log, QD_LOG_CRITICAL, "seq_advanced: Router not found");
break;
}
//
// Raise the event to be picked up by core modules.
//
qdrc_event_router_raise(core, QDRC_EVENT_ROUTER_MOBILE_SEQ_ADVANCED, rnode);
} while (false);
}
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_iterator_copy(address->iterator);
qd_log(core->log, QD_LOG_INFO, "In-process subscription %c/%s", aclass, astring);
free(astring);
qd_iterator_annotate_prefix(address->iterator, aclass);
if (aclass == 'M')
qd_iterator_annotate_phase(address->iterator, phase);
qd_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, 0);
if (addr) {
qd_hash_insert(core->addr_hash, address->iterator, addr, &addr->hash_handle);
DEQ_ITEM_INIT(addr);
DEQ_INSERT_TAIL(core->addrs, addr);
}
}
if (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);
}
free(sub);
}
//==================================================================================
// Call-back Functions
//==================================================================================
static void qdr_do_set_mobile_seq(qdr_core_t *core, qdr_general_work_t *work)
{
core->rt_set_mobile_seq(core->rt_context, work->maskbit, work->mobile_seq);
}
static void qdr_do_set_my_mobile_seq(qdr_core_t *core, qdr_general_work_t *work)
{
core->rt_set_my_mobile_seq(core->rt_context, work->mobile_seq);
}
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_set_mobile_seq_CT(qdr_core_t *core, int router_maskbit, uint64_t mobile_seq)
{
qdr_general_work_t *work = qdr_general_work(qdr_do_set_mobile_seq);
work->mobile_seq = mobile_seq;
work->maskbit = router_maskbit;
qdr_post_general_work_CT(core, work);
}
void qdr_post_set_my_mobile_seq_CT(qdr_core_t *core, uint64_t mobile_seq)
{
qdr_general_work_t *work = qdr_general_work(qdr_do_set_my_mobile_seq);
work->mobile_seq = mobile_seq;
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);
}