in src/router_core/modules/mobile_sync/mobile.c [463:696]
static void qcm_mobile_sync_on_mau_CT(qdrm_mobile_sync_t *msync, qd_parsed_field_t *body)
{
if (!!body && qd_parse_is_map(body)) {
qd_parsed_field_t *id_field = qd_parse_value_by_key(body, ID);
qd_parsed_field_t *mobile_seq_field = qd_parse_value_by_key(body, MOBILE_SEQ);
if (!id_field || !mobile_seq_field)
return;
uint64_t mobile_seq = qd_parse_as_ulong(mobile_seq_field);
qd_parsed_field_t *version_field = qd_parse_value_by_key(body, PV);
uint32_t version = version_field ? qd_parse_as_uint(version_field) : 0;
qdr_node_t *router = qdc_mobile_sync_router_by_id(msync, id_field);
if (!!router) {
const char *router_id = (const char*) qd_hash_key_by_handle(router->owning_addr->hash_handle) + 1;
if (version > PROTOCOL_VERSION) {
if (!BIT_IS_SET(router->sync_mask, ADDR_SYNC_ROUTER_VERSION_LOGGED)) {
BIT_SET(router->sync_mask, ADDR_SYNC_ROUTER_VERSION_LOGGED);
qd_log(msync->log, QD_LOG_WARNING, "Received MAU at protocol version %"PRIu32" from %s. Ignoring.",
version, router_id);
}
return;
}
qd_parsed_field_t *add_field = qd_parse_value_by_key(body, ADD);
qd_parsed_field_t *del_field = qd_parse_value_by_key(body, DEL);
qd_parsed_field_t *exist_field = qd_parse_value_by_key(body, EXIST);
qd_parsed_field_t *hints_field = qd_parse_value_by_key(body, HINTS);
uint32_t hints_count = 0;
qdr_address_t *addr;
//
// Validate the fields and determine what kind of MAU we've received.
//
if (!!hints_field && qd_parse_is_list(hints_field))
hints_count = qd_parse_sub_count(hints_field);
//
// Validate the exist, add, and del fields. They must, if they exist, be lists.
// If there is an exist field, there must not be an add or del field.
// If there is no exist field, there must be both an add and a del field.
//
if ((!!exist_field && !qd_parse_is_list(exist_field))
|| (!!add_field && !qd_parse_is_list(add_field))
|| (!!del_field && !qd_parse_is_list(del_field))
|| (!!exist_field && (!!add_field || !!del_field))
|| (!exist_field && (!add_field || !del_field))) {
qd_log(msync->log, QD_LOG_ERROR, "Received malformed MAU from %s", router_id);
return;
}
//
// If this is a differential MAU and it doesn't represent the next expected
// update, treat this like a sequence-advance and send a MAR
//
if (!exist_field && router->mobile_seq != mobile_seq - 1) {
if (!BIT_IS_SET(router->sync_mask, ADDR_SYNC_ROUTER_MA_REQUESTED)) {
qcm_mobile_sync_on_router_advanced_CT(msync, router);
BIT_SET(router->sync_mask, ADDR_SYNC_ROUTER_MA_REQUESTED);
}
return;
}
//
// Record the new mobile sequence for the remote router.
//
BIT_CLEAR(router->sync_mask, ADDR_SYNC_ROUTER_MA_REQUESTED);
router->mobile_seq = mobile_seq;
//
// Check the exist/add list size against the hints-list size. If they are not
// exactly equal, ignore the hints.
//
if (!!exist_field) {
if (hints_count != qd_parse_sub_count(exist_field))
hints_count = 0;
} else {
if (hints_count != qd_parse_sub_count(add_field))
hints_count = 0;
}
qd_log(msync->log, QD_LOG_DEBUG, "Received MAU (%s) from %s, mobile_seq=%"PRIu64,
!!exist_field ? "absolute" : "differential", router_id, mobile_seq);
//
// If this is an absolute MAU, the existing set of addresses for this router must
// be marked as needing deletion, in case they are not mentioned in the existing
// address list.
//
if (!!exist_field) {
addr = DEQ_HEAD(msync->core->addrs);
while (!!addr) {
if (qcm_mobile_sync_addr_is_mobile(addr) && !!qd_bitmask_value(addr->rnodes, router->mask_bit))
BIT_SET(addr->sync_mask, ADDR_SYNC_ADDRESS_TO_BE_DELETED);
addr = DEQ_NEXT(addr);
}
}
//
// Run through the add/exist list (depending on which we have) and lookup/add the
// addresses, associating them with the sending router. Clear the to-delete bits
// on every address touched. If hints are available, use them for addresses that
// are newly created.
//
qd_parsed_field_t *field = !!exist_field ? exist_field : add_field;
qd_parsed_field_t *addr_field = qd_field_first_child(field);
qd_parsed_field_t *hint_field = !!hints_count ? qd_field_first_child(hints_field) : 0;
while (addr_field) {
qd_iterator_t *iter = qd_parse_raw(addr_field);
qdr_address_t *addr = 0;
int treatment_hint = !!hint_field ? qd_parse_as_int(hint_field) : -1;
do {
qd_hash_retrieve(msync->core->addr_hash, iter, (void**) &addr);
if (!addr) {
qdr_address_config_t *addr_config;
qd_address_treatment_t treatment =
qdr_treatment_for_address_hash_with_default_CT(msync->core,
iter,
qcm_mobile_sync_default_treatment(msync->core, treatment_hint),
&addr_config);
addr = qdr_address_CT(msync->core, treatment, addr_config);
if (!addr) {
qd_log(msync->log, QD_LOG_CRITICAL, "map_destination: ignored");
assert(false);
break;
}
qd_hash_insert(msync->core->addr_hash, iter, addr, &addr->hash_handle);
DEQ_ITEM_INIT(addr);
DEQ_INSERT_TAIL(msync->core->addrs, addr);
//
// if the address is a link route, add the pattern to the wildcard
// address parse tree
//
const char *a_str = (const char*) qd_hash_key_by_handle(addr->hash_handle);
if (QDR_IS_LINK_ROUTE(a_str[0])) {
qdr_link_route_map_pattern_CT(msync->core, iter, addr);
}
}
BIT_CLEAR(addr->sync_mask, ADDR_SYNC_ADDRESS_TO_BE_DELETED);
if (!qd_bitmask_value(addr->rnodes, router->mask_bit)) {
qd_bitmask_set_bit(addr->rnodes, router->mask_bit);
router->ref_count++;
addr->cost_epoch--;
qdr_addr_start_inlinks_CT(msync->core, addr);
//
// Raise an address event if this is the first destination for the address
//
if (qd_bitmask_cardinality(addr->rnodes) + DEQ_SIZE(addr->rlinks) == 1)
qdrc_event_addr_raise(msync->core, QDRC_EVENT_ADDR_BECAME_DEST, addr);
else if (qd_bitmask_cardinality(addr->rnodes) == 1 && DEQ_SIZE(addr->rlinks) == 1)
qdrc_event_addr_raise(msync->core, QDRC_EVENT_ADDR_TWO_DEST, addr);
}
} while (false);
addr_field = qd_field_next_child(addr_field);
hint_field = !!hint_field ? qd_field_next_child(hint_field) : 0;
}
//
// Run through the delete list, if it exists, and disassociate each address from the
// sending router. Check the address to see if it needs to be deleted.
//
if (!!del_field) {
addr_field = qd_field_first_child(del_field);
while (!!addr_field) {
qd_iterator_t *iter = qd_parse_raw(addr_field);
qdr_address_t *addr = 0;
qd_hash_retrieve(msync->core->addr_hash, iter, (void**) &addr);
if (!!addr) {
if (qd_bitmask_value(addr->rnodes, router->mask_bit)) {
qd_bitmask_clear_bit(addr->rnodes, router->mask_bit);
router->ref_count--;
addr->cost_epoch--;
//
// Raise an address event if this was the last destination for the address
//
if (qd_bitmask_cardinality(addr->rnodes) + DEQ_SIZE(addr->rlinks) == 0)
qdrc_event_addr_raise(msync->core, QDRC_EVENT_ADDR_NO_LONGER_DEST, addr);
else if (qd_bitmask_cardinality(addr->rnodes) == 0 && DEQ_SIZE(addr->rlinks) == 1)
qdrc_event_addr_raise(msync->core, QDRC_EVENT_ADDR_ONE_LOCAL_DEST, addr);
qdr_check_addr_CT(msync->core, addr);
}
}
addr_field = qd_field_next_child(addr_field);
}
}
//
// If this was an absolute MAU, disassociate any addresses remaining with the
// to-delete flag set.
//
if (!!exist_field) {
addr = DEQ_HEAD(msync->core->addrs);
while (!!addr) {
qdr_address_t *next_addr = DEQ_NEXT(addr);
if (qcm_mobile_sync_addr_is_mobile(addr)
&& !!qd_bitmask_value(addr->rnodes, router->mask_bit)
&& BIT_IS_SET(addr->sync_mask, ADDR_SYNC_ADDRESS_TO_BE_DELETED)) {
qd_bitmask_clear_bit(addr->rnodes, router->mask_bit);
router->ref_count--;
addr->cost_epoch--;
//
// Raise an address event if this was the last destination for the address
//
if (qd_bitmask_cardinality(addr->rnodes) + DEQ_SIZE(addr->rlinks) == 0)
qdrc_event_addr_raise(msync->core, QDRC_EVENT_ADDR_NO_LONGER_DEST, addr);
else if (qd_bitmask_cardinality(addr->rnodes) == 0 && DEQ_SIZE(addr->rlinks) == 1)
qdrc_event_addr_raise(msync->core, QDRC_EVENT_ADDR_ONE_LOCAL_DEST, addr);
qdr_check_addr_CT(msync->core, addr);
}
addr = next_addr;
}
}
//
// Tell the python router about the new mobile sequence
//
qdr_post_set_mobile_seq_CT(msync->core, router->mask_bit, mobile_seq);
} else {
log_unknown_router(msync, id_field, "MAU");
}
}
}