Exemple #1
0
/**
 * This is called as sendMessage() by the switch.
 * There is only one switch interface which sends all traffic.
 * message is aligned on the beginning of the switch header.
 */
static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf)
{
    struct Ducttape* context = switchIf->senderContext;
    struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) message->bytes;
    Message_shift(message, -Headers_SwitchHeader_SIZE);

    // The label comes in reversed from the switch because the switch doesn't know that we aren't
    // another switch ready to parse more bits, bit reversing the label yields the source address.
    switchHeader->label_be = Bits_bitReverse64(switchHeader->label_be);

    if (Headers_getMessageType(switchHeader) == Headers_SwitchHeader_TYPE_CONTROL) {
        uint8_t labelStr[20];
        uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be);
        AddrTools_printPath(labelStr, label);
        if (message->length < Control_HEADER_SIZE) {
            Log_info1(context->logger, "dropped runt ctrl packet from [%s]", labelStr);
            return Error_NONE;
        } else {
            Log_debug1(context->logger, "ctrl packet from [%s]", labelStr);
        }
        struct Control* ctrl = (struct Control*) message->bytes;
        bool pong = false;
        if (ctrl->type_be == Control_ERROR_be) {
            if (message->length < Control_Error_MIN_SIZE) {
                Log_info1(context->logger, "dropped runt error packet from [%s]", labelStr);
                return Error_NONE;
            }
            Log_info2(context->logger,
                      "error packet from [%s], error type [%d]",
                      labelStr,
                      Endian_bigEndianToHost32(ctrl->content.error.errorType_be));

            RouterModule_brokenPath(Endian_bigEndianToHost64(switchHeader->label_be),
                                    context->routerModule);

            uint8_t causeType = Headers_getMessageType(&ctrl->content.error.cause);
            if (causeType == Headers_SwitchHeader_TYPE_CONTROL) {
                if (message->length < Control_Error_MIN_SIZE + Control_HEADER_SIZE) {
                    Log_info1(context->logger,
                              "error packet from [%s] containing runt cause packet",
                              labelStr);
                    return Error_NONE;
                }
                struct Control* causeCtrl = (struct Control*) &(&ctrl->content.error.cause)[1];
                if (causeCtrl->type_be != Control_PING_be) {
                    Log_info3(context->logger,
                              "error packet from [%s] caused by [%s] packet ([%d])",
                              labelStr,
                              Control_typeString(causeCtrl->type_be),
                              Endian_bigEndianToHost16(causeCtrl->type_be));
                } else {
                    Log_debug2(context->logger,
                               "error packet from [%s] in response to ping, length: [%d].",
                               labelStr,
                               message->length);
                    // errors resulting from pings are forwarded back to the pinger.
                    pong = true;
                }
            } else if (causeType != Headers_SwitchHeader_TYPE_DATA) {
                Log_info1(context->logger,
                          "error packet from [%s] containing cause of unknown type [%d]",
                          labelStr);
            }
        } else if (ctrl->type_be == Control_PONG_be) {
            pong = true;
        } else if (ctrl->type_be == Control_PING_be) {
            ctrl->type_be = Control_PONG_be;
            Message_shift(message, Headers_SwitchHeader_SIZE);
            switchIf->receiveMessage(message, switchIf);
        } else {
            Log_info2(context->logger,
                      "control packet of unknown type from [%s], type [%d]",
                      labelStr, Endian_bigEndianToHost16(ctrl->type_be));
        }

        if (pong) {
            // Shift back over the header
            Message_shift(message, Headers_SwitchHeader_SIZE);
            context->switchPingerIf->receiveMessage(message, context->switchPingerIf);
        }
        return Error_NONE;
    }

    uint8_t* herKey = extractPublicKey(message, switchHeader->label_be, context->logger);
    int herAddrIndex;
    if (herKey) {
        uint8_t herAddrStore[16];
        AddressCalc_addressForPublicKey(herAddrStore, herKey);
        if (herAddrStore[0] != 0xFC) {
            Log_debug(context->logger,
                      "Got message from peer whose address is not in fc00::/8 range.\n");
            return 0;
        }
        herAddrIndex = AddressMapper_put(switchHeader->label_be, herAddrStore, &context->addrMap);
    } else {
        herAddrIndex = AddressMapper_indexOf(switchHeader->label_be, &context->addrMap);
        if (herAddrIndex == -1) {
            uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be);
            struct Node* n = RouterModule_getNode(label, context->routerModule);
            if (n) {
                herAddrIndex = AddressMapper_put(switchHeader->label_be,
                                                 n->address.ip6.bytes,
                                                 &context->addrMap);
            } else {
                #ifdef Log_DEBUG
                    uint8_t switchAddr[20];
                    AddrTools_printPath(switchAddr, Endian_bigEndianToHost64(switchHeader->label_be));
                    Log_debug1(context->logger,
                               "Dropped traffic packet from unknown node. (%s)\n",
                               &switchAddr);
                #endif
                return 0;
            }
        }
    }

    // If the source address is the same as the router address, no third layer of crypto.
    context->routerAddress = context->addrMap.entries[herAddrIndex].address;

    // This is needed so that the priority and other information
    // from the switch header can be passed on properly.
    context->switchHeader = switchHeader;

    context->session = SessionManager_getSession(context->routerAddress, herKey, context->sm);

    // This goes to incomingFromCryptoAuth()
    // then incomingFromRouter() then core()
    context->layer = OUTER_LAYER;
    context->session->receiveMessage(message, context->session);

    return 0;
}
/*********************************************************************
 * @fn      Data_Service_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 * @param   method - type of write message
 *
 * @return  SUCCESS, blePending or Failure
 */
static bStatus_t Data_Service_WriteAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                        uint8_t *pValue, uint16_t len, uint16_t offset,
                                        uint8_t method )
{
  bStatus_t status  = SUCCESS;
  uint8_t   paramID = 0xFF;
  uint8_t   changeParamID = 0xFF;
  uint16_t writeLenMin;
  uint16_t writeLenMax;
  uint16_t *pValueLenVar;

  // See if request is regarding a Client Characterisic Configuration
  if (ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
  {
    Log_info3("WriteAttrCB (CCCD): param: %d connHandle: %d %s",
              (IArg)Data_Service_findCharParamId(pAttr),
              (IArg)connHandle,
              (IArg)(method == GATT_LOCAL_WRITE?"- restoring bonded state":"- OTA write"));

    // Allow notification and indication, but do not check if really allowed per CCCD.
    status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                             offset, GATT_CLIENT_CFG_NOTIFY |
                                                     GATT_CLIENT_CFG_INDICATE );
    if (SUCCESS == status && pAppCBs && pAppCBs->pfnCfgChangeCb)
       pAppCBs->pfnCfgChangeCb( connHandle, DATA_SERVICE_SERV_UUID,
                                Data_Service_findCharParamId(pAttr), pValue, len );

     return status;
  }

  // Find settings for the characteristic to be written.
  paramID = Data_Service_findCharParamId( pAttr );
  switch ( paramID )
  {
    case DS_STRING_ID:
      writeLenMin  = DS_STRING_LEN_MIN;
      writeLenMax  = DS_STRING_LEN;
      pValueLenVar = &ds_StringValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"String",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for String can be inserted here */
      break;

    case DS_STREAM_ID:
      writeLenMin  = DS_STREAM_LEN_MIN;
      writeLenMax  = DS_STREAM_LEN;
      pValueLenVar = &ds_StreamValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"Stream",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Stream can be inserted here */
      break;

    default:
      Log_error0("Attribute was not found.");
      return ATT_ERR_ATTR_NOT_FOUND;
  }
  // Check whether the length is within bounds.
  if ( offset >= writeLenMax )
  {
    Log_error0("An invalid offset was requested.");
    status = ATT_ERR_INVALID_OFFSET;
  }
  else if ( offset + len > writeLenMax )
  {
    Log_error0("Invalid value length was received.");
    status = ATT_ERR_INVALID_VALUE_SIZE;
  }
  else if ( offset + len < writeLenMin && ( method == ATT_EXECUTE_WRITE_REQ || method == ATT_WRITE_REQ ) )
  {
    // Refuse writes that are lower than minimum.
    // Note: Cannot determine if a Reliable Write (to several chars) is finished, so those will
    //       only be refused if this attribute is the last in the queue (method is execute).
    //       Otherwise, reliable writes are accepted and parsed piecemeal.
    Log_error0("Invalid value length was received.");
    status = ATT_ERR_INVALID_VALUE_SIZE;
  }
  else
  {
    // Copy pValue into the variable we point to from the attribute table.
    memcpy(pAttr->pValue + offset, pValue, len);

    // Only notify application and update length if enough data is written.
    //
    // Note: If reliable writes are used (meaning several attributes are written to using ATT PrepareWrite),
    //       the application will get a callback for every write with an offset + len larger than _LEN_MIN.
    // Note: For Long Writes (ATT Prepare + Execute towards only one attribute) only one callback will be issued,
    //       because the write fragments are concatenated before being sent here.
    if ( offset + len >= writeLenMin )
    {
      changeParamID = paramID;
      *pValueLenVar = offset + len; // Update data length.
    }
  }

  // Let the application know something changed (if it did) by using the
  // callback it registered earlier (if it did).
  if (changeParamID != 0xFF)
    if ( pAppCBs && pAppCBs->pfnChangeCb )
      pAppCBs->pfnChangeCb( connHandle, DATA_SERVICE_SERV_UUID, paramID, pValue, len+offset ); // Call app function from stack task context.

  return status;
}