static void qcm_mobile_sync_on_mau_CT()

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");
        }
    }
}