コード例 #1
0
/* This function called periodically to find expired pending elements */
void zb_nwk_mesh_expiry_pending(zb_uint8_t param) 
{
  ZVUNUSED(param);
  TRACE_MSG(TRACE_NWK1, ">> exp_pend", (FMT__0));

  if ( ZG->nwk.nib.pending_table_cnt )
  {
    zb_ushort_t i;

    for (i = 0; i < ZB_NWK_PENDING_TABLE_SIZE; i++)
    {
      if ( ZG->nwk.nib.pending_table[i].used )
      {
        TRACE_MSG(TRACE_NWK1, "pend ent %d dst_addr %d exp %d", (FMT__D_D_D,
                  i, ZG->nwk.nib.pending_table[i].dest_addr, ZG->nwk.nib.pending_table[i].expiry));
        if ( ZG->nwk.nib.pending_table[i].expiry == 0 )
        {
          TRACE_MSG(TRACE_NWK1, "free buf %p", (FMT__P, ZG->nwk.nib.pending_table[i].buf));
          zb_free_buf(ZG->nwk.nib.pending_table[i].buf);
          NWK_ARRAY_PUT_ENT(ZG->nwk.nib.pending_table, &ZG->nwk.nib.pending_table[i], ZG->nwk.nib.pending_table_cnt);
        }
        else
        {
          ZG->nwk.nib.pending_table[i].expiry--;
        }
      }
    }
  }

  /* Schedule to call later */
  ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_pending, 0, ZB_NWK_EXPIRY_PENDING);

  TRACE_MSG(TRACE_NWK1, "<< exp_pend", (FMT__0));
}
コード例 #2
0
/* This function is called when we got new buffer to initiate route discovery */
void zb_nwk_mesh_initiate_route_discovery(zb_uint8_t param) 
{
  zb_nwk_pend_t *ent;
  zb_buf_t *cbuf = (zb_buf_t *)ZB_BUF_FROM_REF(param);

  TRACE_MSG(TRACE_NWK1, ">> initiate_route_discovery %hd", (FMT__H, param));

  NWK_ARRAY_FIND_ENT(ZG->nwk.nib.pending_table, ent, ent->waiting_buf);
  if ( ent )
  {
    zb_nwk_hdr_t *nwhdr = (zb_nwk_hdr_t *)ZB_BUF_BEGIN(ent->buf);

    zb_nwk_mesh_route_discovery(cbuf, nwhdr->dst_addr, 0);
    ent->waiting_buf = 0;
  }
  else
  {
    zb_free_buf(cbuf);
    TRACE_MSG(TRACE_NWK1, "perepil", (FMT__0));
    ZB_ASSERT(0);
  }

  TRACE_MSG(TRACE_NWK1, "<< initiate_route_discovery", (FMT__0));
}
コード例 #3
0
/*
 * Process an incoming route reply.
 * First we need to make sure that we have a discovery table and routing table
 * entries for this route reply. If not or if path cost is greater than what we
 * have, then discard route reply.
 * If the route reply is for us, then end the route discovery process and send
 * out any frames that are buffered in the pending list. Otherwise, forward the
 * route reply to the sender of the route request as recorded in the discover
 * table entry.
 */
void zb_nwk_mesh_rrep_handler(zb_buf_t *buf, zb_nwk_hdr_t *nwk_hdr, zb_nwk_cmd_rrep_t *nwk_cmd_rrep) 
{
  zb_uint8_t path_cost;
  zb_nwk_route_discovery_t *disc_ent;
  zb_nwk_routing_t *routing_ent;
  zb_uint16_t src_addr;

  TRACE_MSG(TRACE_NWK1, ">> rrep_handler buf %p nwk_hdr %p nwk_cmd_rrep %p", (FMT__P_P_P, buf, nwk_hdr, nwk_cmd_rrep));

  /* parse mac header to get source address */
  nwk_get_mac_source_addr(buf, &src_addr);

  /* find proper discovery and routing table entries, calculate path cost */
  TRACE_MSG(TRACE_NWK1, "rrep cmd: rreq_id %hd orig %d resp %d", (FMT__H_D_D,
            nwk_cmd_rrep->rreq_id, nwk_cmd_rrep->originator, nwk_cmd_rrep->responder));

  /* clac path cost */
  NWK_CALC_PATH_COST(nwk_hdr->src_addr, path_cost);
  TRACE_MSG(TRACE_NWK1, "path_cost %hd", (FMT__H, path_cost));
  path_cost += nwk_cmd_rrep->path_cost;
  TRACE_MSG(TRACE_NWK1, "total path_cost %hd", (FMT__H, path_cost));
  ZB_NWK_ADDR_TO_LE16(nwk_cmd_rrep->responder);
  ZB_NWK_ADDR_TO_LE16(nwk_cmd_rrep->originator);
  NWK_ARRAY_FIND_ENT( ZG->nwk.nib.route_disc_table, disc_ent,
                      (disc_ent->request_id == nwk_cmd_rrep->rreq_id) && (disc_ent->source_addr == nwk_cmd_rrep->originator) );
  NWK_ARRAY_FIND_ENT( ZG->nwk.nib.routing_table, routing_ent,
                      (routing_ent->dest_addr == nwk_cmd_rrep->responder) );
  TRACE_MSG(TRACE_NWK1, "p_cost %d disc_ent %p r_ent %p residual_cost %hd", (FMT__D_P_P_H,\
            path_cost, disc_ent, routing_ent, disc_ent ? disc_ent->residual_cost : 0));

  /* check response is meaningful for us */
  if ( !disc_ent
       || !routing_ent
       || path_cost > disc_ent->residual_cost )
  {
    TRACE_MSG(TRACE_NWK1, "drop rresp, no purpose ent fnd or path too long", (FMT__0));
    if ( !disc_ent || !routing_ent )
    {
      TRACE_MSG(TRACE_NWK1, "one of disc or r ent is absent, free other", (FMT__0));
      if ( disc_ent )
      {
        NWK_ARRAY_PUT_ENT(ZG->nwk.nib.route_disc_table, disc_ent, ZG->nwk.nib.route_disc_table_cnt);
      }
      if ( routing_ent )
      {
        NWK_ARRAY_PUT_ENT(ZG->nwk.nib.routing_table, routing_ent, ZG->nwk.nib.routing_table_cnt);
      }
    }
    goto done;
  }

  /* update route and discovery entries */
  disc_ent->residual_cost    = path_cost;
  disc_ent->expiration_time  = ZB_NWK_ROUTE_DISCOVERY_EXPIRY;
  routing_ent->next_hop_addr = src_addr;
  routing_ent->status = (routing_ent->status == ZB_NWK_ROUTE_STATE_DISCOVERY_UNDERWAY) ? ZB_NWK_ROUTE_STATE_VALIDATION_UNDERWAY : routing_ent->status;

  /* check if rrep is for us */
  if ( nwk_cmd_rrep->originator == ZB_NIB_NETWORK_ADDRESS() )
  {
    zb_ushort_t i;
    TRACE_MSG(TRACE_NWK1, "got rrepl for our rreq, snd wait pckts", (FMT__0));

    /* Route discovery is complete. Now we have a new route. Go throught pending
     * queue to find packet to be forwarded */
    for(i = 0; i < ZB_NWK_PENDING_TABLE_SIZE; i++)
    {
      if ( ZG->nwk.nib.pending_table[i].used
           && ZG->nwk.nib.pending_table[i].dest_addr == routing_ent->dest_addr )
      {
        TRACE_MSG(TRACE_NWK1, "fnd pkt to %d addr, send it", (FMT__D, routing_ent->dest_addr));
        ZB_SET_BUF_PARAM(ZG->nwk.nib.pending_table[i].buf, ZG->nwk.nib.pending_table[i].handle, zb_uint8_t);
        ZB_SCHEDULE_CALLBACK(zb_nwk_forward, ZB_REF_FROM_BUF(ZG->nwk.nib.pending_table[i].buf));
        NWK_ARRAY_PUT_ENT(ZG->nwk.nib.pending_table, &ZG->nwk.nib.pending_table[i], ZG->nwk.nib.pending_table_cnt);
      }
    }

    /* if the request was initiated by APS, confirm that route discovery failed */
    TRACE_MSG(TRACE_NWK1, "aps_rreq_addr %d  dst_addr %d", (FMT__D_D, ZG->nwk.nib.aps_rreq_addr, routing_ent->dest_addr));
    if ( ZG->nwk.nib.aps_rreq_addr == routing_ent->dest_addr )
    {
#ifndef ZB_LIMITED_FEATURES
      NWK_ROUTE_DISCOVERY_CONFIRM(buf, ZB_NWK_STATUS_SUCCESS, 0xff);
#endif
      ZG->nwk.nib.aps_rreq_addr = -1;
    }
  }
  else
  {
    /* forward */
    TRACE_MSG(TRACE_NWK1, "frwd rrep to the %d address", (FMT__D, disc_ent->sender_addr));
    zb_nwk_mesh_send_rrep(buf, nwk_cmd_rrep->rreq_id, nwk_cmd_rrep->originator, nwk_cmd_rrep->responder, path_cost, disc_ent->sender_addr);
    /* zb_nwk_mesh_send_rrep is now responsible for buf */
    buf = NULL;
  }

  done:
  if ( buf )
  {
    TRACE_MSG(TRACE_NWK1, "free buf %p", (FMT__P, buf));
    zb_free_buf(buf);
  }
  TRACE_MSG(TRACE_NWK1, "<< rrep_handler", (FMT__0));
}
コード例 #4
0
/*
  Process an incoming route request and decide if it needs to be forwarded
  or a route reply needs to be generated. This function will check the
  discovery table to see if we received the same route request previously.
  If we did, then it will compare the path cost to see if the incoming route
  request has a more efficient path. If so, then it will replace the discovery
  entry info with the info from this route request.

  If its a new route request, then it will create a route discovery entry and a
  routing table entry. It will then check to see if its the destination. If so, then
  a route reply will be sent. Otherwise, it will relay the route request via
  broadcast.
*/
void zb_nwk_mesh_rreq_handler(zb_buf_t *buf, zb_nwk_hdr_t *nwk_hdr, zb_nwk_cmd_rreq_t *nwk_cmd_rreq) 
{
  zb_uint8_t ret = RET_OK;
  zb_uint8_t path_cost = 0;
  zb_nwk_route_discovery_t *disc_ent = NULL;
  zb_nwk_routing_t *routing_ent = NULL;

  TRACE_MSG(TRACE_NWK1, ">> rreq_handler buf %p hdr %p cmd %p", (FMT__P_P_P, buf, nwk_hdr, nwk_cmd_rreq));

  /* calc path cost */
  NWK_CALC_PATH_COST(nwk_hdr->src_addr, path_cost);
  TRACE_MSG(TRACE_NWK1, "path_cost %hd", (FMT__H, path_cost));
  path_cost += nwk_cmd_rreq->path_cost;
  TRACE_MSG(TRACE_NWK1, "total path_cost %hd", (FMT__H, path_cost));

  /* search in routing table */
  TRACE_MSG(TRACE_NWK1, "dst_addr %d", (FMT__D, nwk_cmd_rreq->dest_addr));
  NWK_ARRAY_FIND_ENT( ZG->nwk.nib.routing_table, routing_ent, (routing_ent->dest_addr == nwk_cmd_rreq->dest_addr) );
  if ( routing_ent )
  {
    TRACE_MSG(TRACE_NWK1, "r ent fnd, state %d", (FMT__D, routing_ent->status));
    if ( (routing_ent->status != ZB_NWK_ROUTE_STATE_ACTIVE)
         && (routing_ent->status != ZB_NWK_ROUTE_STATE_VALIDATION_UNDERWAY) )
    {
      routing_ent->status = ZB_NWK_ROUTE_STATE_DISCOVERY_UNDERWAY;
      TRACE_MSG(TRACE_NWK1, "change r ent state to dsc uway", (FMT__0));
    }
  }
  else
  {
    TRACE_MSG(TRACE_NWK1, "no routing enttry found, add new", (FMT__0));
    /* add new routing entry */
    NWK_ARRAY_GET_ENT(ZG->nwk.nib.routing_table, routing_ent, ZG->nwk.nib.routing_table_cnt);
    if ( routing_ent )
    {
      routing_ent->dest_addr = nwk_cmd_rreq->dest_addr;
      routing_ent->status = ZB_NWK_ROUTE_STATE_DISCOVERY_UNDERWAY;
      TRACE_MSG(TRACE_NWK1, "new routing ent : dest_addr %d status %hd", (FMT__D_H,
                routing_ent->dest_addr, routing_ent->status));
    }
    else
    {
      ret = RET_NO_MEMORY;
      TRACE_MSG(TRACE_ERROR, "no room for r ent, skip rreq", (FMT__0));
      goto done;
    }
  }

  /* search thought discovery table */
  TRACE_MSG(TRACE_NWK1, "rreq_id %hd src_addr %d", (FMT__H_D, nwk_cmd_rreq->rreq_id, nwk_hdr->src_addr));
  NWK_ARRAY_FIND_ENT( ZG->nwk.nib.route_disc_table, disc_ent,
                      (disc_ent->request_id == nwk_cmd_rreq->rreq_id) && (disc_ent->source_addr == nwk_hdr->src_addr) );
  if ( disc_ent )
  {
    TRACE_MSG(TRACE_NWK1, "dsc entry found, path_cost %hd forward_cost %hd", (FMT__H_H, path_cost, disc_ent->forward_cost));
    /* check path cost and update route disc entry */
    if ( path_cost < disc_ent->forward_cost )
    {
      zb_uint16_t src_addr;
      nwk_get_mac_source_addr(buf, &src_addr);

      TRACE_MSG(TRACE_NWK1, "new forward cost %hd node %d old %d", (FMT__H_D_D, path_cost, src_addr, disc_ent->sender_addr));
      /* replace route with better one */
      disc_ent->sender_addr = src_addr;
      disc_ent->forward_cost = path_cost;
    }
    else
    {
      /* this is normal case, do not change ret code */
      TRACE_MSG(TRACE_NWK1, "we have better path, skip rreq", (FMT__0));
      goto done;
    }
  }
  else
  {
    TRACE_MSG(TRACE_NWK1, "no disc ent, try to add new", (FMT__0));
    /* add new discovery entry */
    NWK_ARRAY_GET_ENT(ZG->nwk.nib.route_disc_table, disc_ent, ZG->nwk.nib.route_disc_table_cnt);
    if ( disc_ent )
    {
      zb_uint16_t src_addr;
      nwk_get_mac_source_addr(buf, &src_addr);

      disc_ent->request_id       = nwk_cmd_rreq->rreq_id;
      disc_ent->source_addr      = nwk_hdr->src_addr;
      disc_ent->sender_addr      = src_addr;
      disc_ent->dest_addr        = nwk_cmd_rreq->dest_addr;
      disc_ent->forward_cost     = path_cost;
      disc_ent->residual_cost    = 0xff;
      disc_ent->expiration_time  = ZB_NWK_ROUTE_DISCOVERY_EXPIRY;
      TRACE_MSG(TRACE_NWK1, "disc ent added: request_id %hd src_addr %d sndr_addr %d d_addr %d fwd_cost %hd res_cost %hd exp_time %d", (FMT__H_D_D_D_H_H_D,
                disc_ent->request_id, disc_ent->source_addr, disc_ent->sender_addr, disc_ent->dest_addr,
                disc_ent->forward_cost, disc_ent->residual_cost, disc_ent->expiration_time));
    }
    else
    {
      ret = RET_NO_MEMORY;
      TRACE_MSG(TRACE_ERROR, "no room f/disc ent, skip rreq", (FMT__0));
      goto done;
    }
  }

  /* check if this rreq is for us or our neighbour, send route reply in this
   * case, forward route request otherwise */
  {
    zb_neighbor_tbl_ent_t *nbt;

    if ( nwk_cmd_rreq->dest_addr == ZB_NIB_NETWORK_ADDRESS()
         || zb_nwk_neighbor_get_by_short(nwk_cmd_rreq->dest_addr, &nbt) == RET_OK )
    {
      zb_uint16_t src_addr;
      nwk_get_mac_source_addr(buf, &src_addr);

      TRACE_MSG(TRACE_ERROR, "this rreq f/us or neighbor, snd rrep", (FMT__0));
      zb_nwk_mesh_send_rrep(buf, nwk_cmd_rreq->rreq_id, nwk_hdr->src_addr, nwk_cmd_rreq->dest_addr, path_cost, src_addr);
      /* zb_nwk_mesh_send_rrep is now responsible for buffer */
      buf = NULL;
    }
    else
    {
      /* Buffer will be modified inside zb_nwk_mesh_send_rreq, save data we
       * need */
      zb_nwk_cmd_rreq_t nwk_cmd_rreq_save;
      ZB_MEMCPY(&nwk_cmd_rreq_save, nwk_cmd_rreq, sizeof(nwk_cmd_rreq_save));

      TRACE_MSG(TRACE_ERROR, "don't know rreq dest, prop it", (FMT__0));
      zb_nwk_mesh_send_rreq(buf, &nwk_cmd_rreq_save, nwk_hdr->src_addr, nwk_hdr->seq_num, path_cost, nwk_hdr->radius);
      /* zb_nwk_mesh_send_rreq is now responsible for buffer */
      buf = NULL;
    }
  }

  done:
  if ( ret != RET_OK )
  {
    TRACE_MSG(TRACE_NWK1, "error %d above, rm disc and r ent if", (FMT__D, ret));
    if ( disc_ent )
    {
      NWK_ARRAY_PUT_ENT(ZG->nwk.nib.route_disc_table, disc_ent, ZG->nwk.nib.route_disc_table_cnt);
    }
    if ( routing_ent )
    {
      NWK_ARRAY_PUT_ENT(ZG->nwk.nib.routing_table, routing_ent, ZG->nwk.nib.routing_table_cnt);
    }

    /* if this request was initiated by APS, confirm that route discovery failed */
    if ( ZG->nwk.nib.aps_rreq_addr
         && ZG->nwk.nib.aps_rreq_addr == nwk_cmd_rreq->dest_addr )
    {
#ifndef ZB_LIMITED_FEATURES
      NWK_ROUTE_DISCOVERY_CONFIRM(buf, ZB_NWK_STATUS_ROUTE_ERROR, ZB_NWK_COMMAND_STATUS_NO_ROUTE_AVAILABLE);
#endif
      /* prevent buffer from being free */
      buf = NULL;
    }
  }

  if ( buf )
  {
    TRACE_MSG(TRACE_NWK1, "free buf %p", (FMT__P, buf));
    zb_free_buf(buf);
  }
  TRACE_MSG(TRACE_NWK1, "<< rreq_handler", (FMT__0));
}
コード例 #5
0
/*
 * Generate and send a route request command frame.
 * Add route request entry into the rreq list to be able to track this request.
*/
static zb_ret_t zb_nwk_mesh_send_rreq(zb_buf_t *cbuf, zb_nwk_cmd_rreq_t *nwk_cmd_rreq, zb_uint16_t src_addr, zb_uint8_t seq_num, zb_uint8_t path_cost, zb_uint8_t radius) 
{
  zb_ret_t ret = RET_OK;

  TRACE_MSG(TRACE_NWK1, ">> send_rreq cbuf %p rreq %p s_addr %d path_cost %hd radius %hd", (FMT__P_P_D_H_H,
                         cbuf, nwk_cmd_rreq, src_addr, path_cost, radius));

#if 0
  /* check we have room in rreq table */
  if ( ZG->nwk.nib.rreq_cnt < ZB_NWK_RREQ_TABLE_SIZE )
#endif
  {
    zb_nwk_hdr_t *nwhdr;
    zb_nwk_cmd_rreq_t *rreq_cmd;
#if 0
    zb_nwk_rreq_t *rreq;
#endif
    zb_bool_t secure = ZB_FALSE;

#ifdef ZB_SECURITY
    secure = (ZG->aps.authenticated && ZG->nwk.nib.secure_all_frames
             && ZG->nwk.nib.security_level);
#endif

    nwhdr = nwk_alloc_and_fill_hdr(cbuf,
                                   ZB_NWK_BROADCAST_ROUTER_COORDINATOR, NULL, NULL, ZB_FALSE, secure, ZB_TRUE);
    rreq_cmd = (zb_nwk_cmd_rreq_t *)nwk_alloc_and_fill_cmd(cbuf, ZB_NWK_CMD_ROUTE_REQUEST, sizeof(zb_nwk_cmd_rreq_t));
    rreq_cmd->opt = 0;
    rreq_cmd->rreq_id = nwk_cmd_rreq->rreq_id;
    rreq_cmd->dest_addr = nwk_cmd_rreq->dest_addr;
    ZB_NWK_ADDR_TO_LE16(rreq_cmd->dest_addr);
    rreq_cmd->path_cost = path_cost;
    nwhdr->radius = radius;
    nwhdr->src_addr = src_addr;

    /* Not sure it is right, but let's assign original seq_num. Else request
     * can be dropped as a dup at receiver's side. */
    if (src_addr != ZB_NIB_NETWORK_ADDRESS())
    {
      nwhdr->seq_num = seq_num;
    }

#if 0
    /* Save info to retransmit request ZB_MWK_RREQ_RETRIES times */
    NWK_ROUTING_ARRAY_GET_ENT(ZG->nwk.nib.rreq, rreq, ZG->nwk.nib.rreq_cnt);
    ZB_ASSERT(rreq);
    if ( rreq )
    {
      rreq->originator = src_addr;
      rreq->radius = radius;
      rreq->retries = 1;
      memcpy(&rreq->cmd, rreq_cmd, sizeof(rreq->cmd));

      /* schedule resend function */
      if ( ZG->nwk.nib.rreq_cnt == 1 )
      {
        ZB_SCHEDULE_ALARM_CANCEL(zb_nwk_mesh_expiry_rreq, ZB_ALARM_ANY_PARAM);
        ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_rreq, -1, ZB_NWK_RREQ_RETRY_INTERVAL);
      }
    }
#endif

    /* transmit route request packet */
    ZB_SET_BUF_PARAM(cbuf, ZB_NWK_INTERNAL_NSDU_HANDLE, zb_uint8_t);
    ZB_SCHEDULE_CALLBACK(zb_nwk_forward, ZB_REF_FROM_BUF(cbuf));
  }
#if 0
  else
  {
    zb_free_buf(cbuf);
    TRACE_MSG(TRACE_NWK1, "rreq buffer is full", (FMT__0));
    ret = RET_NO_MEMORY;
  }
#endif

  TRACE_MSG(TRACE_NWK1, "<< send_rreq %d", (FMT__D, ret));
  return ret;
}
コード例 #6
0
ファイル: apsme_secur.c プロジェクト: Comfee/zboss
/**
   Reaction on TRANSPORT-KEY APS command
 */
void zb_aps_in_transport_key(zb_uint8_t param)
{
  zb_transport_key_nwk_key_dsc_pkt_t *dsc =
    (zb_transport_key_nwk_key_dsc_pkt_t *)ZB_BUF_BEGIN(ZB_BUF_FROM_REF(param));

  TRACE_MSG(TRACE_SECUR3, ">>zb_aps_in_transport_key %d", (FMT__H, param));

  /* See 4.4.3.3  Upon Receipt of a Transport-Key Command */

  switch (dsc->key_type)
  {
    case ZB_STANDARD_NETWORK_KEY:
      if (
        /* key is for me */
        ZB_IEEE_ADDR_CMP(dsc->dest_address, ZB_PIB_EXTENDED_ADDRESS())
        /* key is for all */
          || ZB_IEEE_ADDR_IS_ZERO(dsc->dest_address))
      {
        /* This key is for me. Issue APSME-TRANSPORT-KEY.indication. ZDO will
         * setup keys and remember TC address.
         */
        zb_apsme_transport_key_indication_t *ind = ZB_GET_BUF_PARAM(ZB_BUF_FROM_REF(param), zb_apsme_transport_key_indication_t);

        TRACE_MSG(TRACE_SECUR3, "in std nwk key #%d for me", (FMT__D, dsc->seq_number));
        ind->key_type = dsc->key_type;
        ZB_IEEE_ADDR_COPY(ind->src_address, dsc->source_address);
        ind->key.nwk.key_seq_number = dsc->seq_number;
        ZB_MEMCPY(ind->key.nwk.key, dsc->key, ZB_CCM_KEY_SIZE);
        ZB_SCHEDULE_CALLBACK(zb_apsme_transport_key_indication, param);

/* #ifdef ZB_ROUTER_ROLE */
/* This feature should be processed at request */
#if 0
        if (ZB_IEEE_ADDR_IS_ZERO(dsc->dest_address)
            /* && check for secured transfer at nwk level */
            && ZB_IEEE_ADDR_CMP(dsc->source_address, ZB_AIB().trust_center_address))
        {
          /*
           * Need to pass key to all rx-off-when-idle children. Need another
           * packet buffer for it.
           * Do the rest in the calback: this is blocked
           * buffer alloc. Not need to save current key: it will be aleady
           * assigned, so can send my own key.
           */
          ZG->aps.tmp.neighbor_table_iterator = zb_nwk_neighbor_next_ze_children_rx_off_i(0);
          ZG->aps.tmp.key_seq_number = ind->key.nwk.key_seq_number;
          if (ZG->aps.tmp.neighbor_table_iterator != (zb_ushort_t)~0)
          {
            TRACE_MSG(TRACE_SECUR3, "send key #%hd to all ZE", (FMT__H, dsc->seq_number));
            zb_get_out_buf_delayed(zb_aps_pass_nwk_key_to_children);
          }
        }
#endif  /* ZB_ROUTER_ROLE */
      }
#ifdef ZB_ROUTER_ROLE
      else
      {
        zb_address_ieee_ref_t addr_ref;
        zb_neighbor_tbl_ent_t *nbe;
        /* Search for child in the Neighbor table, mark child as Authenticated,
         * send key to it using unsecured NWK transfer */
        if (zb_address_by_ieee(dsc->dest_address, ZB_FALSE, ZB_FALSE, &addr_ref) == RET_OK
            && zb_nwk_neighbor_get(addr_ref, ZB_FALSE, &nbe) == RET_OK
            && (nbe->relationship == ZB_NWK_RELATIONSHIP_UNAUTHENTICATED_CHILD
                || nbe->relationship == ZB_NWK_RELATIONSHIP_CHILD))
        {
          zb_uint16_t addr;
          zb_address_short_by_ref(&addr, addr_ref);
          TRACE_MSG(TRACE_SECUR3, "send key #%hd to ZE %d, auth ok", (FMT__H_D, dsc->seq_number, addr));
          zb_aps_send_command(param, addr, APS_CMD_TRANSPORT_KEY,
                              (nbe->relationship != ZB_NWK_RELATIONSHIP_UNAUTHENTICATED_CHILD));
          nbe->relationship = ZB_NWK_RELATIONSHIP_CHILD;
        }
        else
        {
          TRACE_MSG(TRACE_SECUR1, "child " TRACE_FORMAT_64 " not found", (FMT__A, TRACE_ARG_64(dsc->dest_address)));
          zb_free_buf(ZB_BUF_FROM_REF(param));
        }
      }
#endif  /* ZB_ROUTER_ROLE */
      break;
    default:
      break;
  }

  TRACE_MSG(TRACE_SECUR3, "<<zb_aps_in_transport_key", (FMT__0));
}