Example #1
0
/*---------------------------------------------------------------------------*/
int coap_context_wait_data(coap_context_t *coap_ctx, int32_t ticks)
{
  struct net_buf *buf;

  buf = net_receive(coap_ctx->net_ctx, ticks);
  if (buf) {
    session_t session;
    int ret;

    uip_ipaddr_copy(&session.addr.ipaddr, &UIP_IP_BUF(buf)->srcipaddr);
    session.addr.port = UIP_UDP_BUF(buf)->srcport;
    session.size = sizeof(session.addr);
    session.ifindex = 1;

    PRINTF("coap-context: got dtls message from ");
    PRINT6ADDR(&session.addr.ipaddr);
    PRINTF(":%d %u bytes\n", uip_ntohs(session.addr.port), uip_appdatalen(buf));

    PRINTF("Received appdata %p appdatalen %d\n",
	   ip_buf_appdata(buf), ip_buf_appdatalen(buf));

    coap_ctx->buf = buf;

    ret = dtls_handle_message(coap_ctx->dtls_context, &session,
			      ip_buf_appdata(buf), ip_buf_appdatalen(buf));

    /* We always release the buffer here as this buffer is never sent
     * to network anyway.
     */
    if (coap_ctx->buf) {
      ip_buf_unref(coap_ctx->buf);
      coap_ctx->buf = NULL;
    }

    return ret;
  }

  return 0;
}
Example #2
0
/*---------------------------------------------------------------------------*/
int coap_context_wait_data(coap_context_t *coap_ctx, int32_t ticks)
{
  struct net_buf *buf;

  buf = net_receive(coap_ctx->net_ctx, ticks);
  if (buf) {
    PRINTF("coap-context: got message from ");
    PRINT6ADDR(&UIP_IP_BUF(buf)->srcipaddr);
    PRINTF(":%d %u bytes\n", uip_ntohs(UIP_UDP_BUF(buf)->srcport),
	   uip_appdatalen(buf));

    PRINTF("Received data appdata %p appdatalen %d\n",
	   ip_buf_appdata(buf), ip_buf_appdatalen(buf));

    coap_ctx->buf = buf;

    coap_engine_receive(coap_ctx);

    return 1;
  }

  return 0;
}
Example #3
0
/* Switch the ports and addresses and set route and neighbor cache.
 * Returns 1 if packet was sent properly, in this case it is the caller
 * that needs to release the net_buf. If 0 is returned, then uIP stack
 * has released the net_buf already because there was an some net related
 * error when sending the buffer.
 */
static inline int udp_prepare_and_send(struct net_context *context,
				       struct net_buf *buf)
{
#ifdef CONFIG_NETWORKING_WITH_IPV6
	uip_ds6_route_t *route_old, *route_new = NULL;
	uip_ds6_nbr_t *nbr;
#endif
	uip_ipaddr_t tmp;
	uint16_t port;
	uint8_t ret;

	if (uip_len(buf) == 0) {
		/* This is expected as uIP will typically set the
		 * packet length to 0 after receiving it. So we need
		 * to fix the length here. The protocol specific
		 * part is added also here.
		 */
		uip_len(buf) = uip_slen(buf) = uip_appdatalen(buf);
		buf->data = buf->buf + UIP_IPUDPH_LEN;
	}

	port = UIP_UDP_BUF(buf)->srcport;
	UIP_UDP_BUF(buf)->srcport = UIP_UDP_BUF(buf)->destport;
	UIP_UDP_BUF(buf)->destport = port;

	uip_ipaddr_copy(&tmp, &UIP_IP_BUF(buf)->srcipaddr);
	uip_ipaddr_copy(&UIP_IP_BUF(buf)->srcipaddr,
			&UIP_IP_BUF(buf)->destipaddr);
	uip_ipaddr_copy(&UIP_IP_BUF(buf)->destipaddr, &tmp);

#ifdef CONFIG_NETWORKING_WITH_IPV6
	/* The peer needs to be in neighbor cache before route can be added.
	 */
	nbr = uip_ds6_nbr_lookup((uip_ipaddr_t *)&UIP_IP_BUF(buf)->destipaddr);
	if (!nbr) {
		const uip_lladdr_t *lladdr = (const uip_lladdr_t *)&buf->src;
		nbr = uip_ds6_nbr_add(
			(uip_ipaddr_t *)&UIP_IP_BUF(buf)->destipaddr,
			lladdr, 0, NBR_REACHABLE);
		if (!nbr) {
			NET_DBG("Cannot add peer ");
			PRINT6ADDR(&UIP_IP_BUF(buf)->destipaddr);
			PRINT(" to neighbor cache\n");
		}
	}

	/* Temporarily add route to peer, delete the route after
	 * sending the packet. Check if there was already a
	 * route and do not remove it if there was existing
	 * route to this peer.
	 */
	route_old = uip_ds6_route_lookup(&UIP_IP_BUF(buf)->destipaddr);
	if (!route_old) {
		route_new = uip_ds6_route_add(&UIP_IP_BUF(buf)->destipaddr,
					      128,
					      &UIP_IP_BUF(buf)->destipaddr);
		if (!route_new) {
			NET_DBG("Cannot add route to peer ");
			PRINT6ADDR(&UIP_IP_BUF(buf)->destipaddr);
			PRINT("\n");
		}
	}
#endif

	ret = simple_udp_sendto_port(buf,
				     net_context_get_udp_connection(context),
				     buf->data, buf->len,
				     &UIP_IP_BUF(buf)->destipaddr,
				     uip_ntohs(UIP_UDP_BUF(buf)->destport));
	if (!ret) {
		NET_DBG("Packet could not be sent properly.\n");
	}

#ifdef CONFIG_NETWORKING_WITH_IPV6
	if (!route_old && route_new) {
		/* This will also remove the neighbor cache entry */
		uip_ds6_route_rm(route_new);
	}
#endif

	return ret;
}
Example #4
0
/*---------------------------------------------------------------------------*/
int
coap_engine_receive(coap_context_t *coap_ctx)
{
  erbium_status_code = NO_ERROR;
  coap_packet_t message[1]; /* this way the packet can be treated as pointer as usual */
  coap_packet_t response[1];
  coap_transaction_t *transaction = NULL;

  PRINTF("%s(): received data len %u\n", __FUNCTION__,
         (uint16_t)uip_appdatalen(coap_ctx->buf));

  if(uip_newdata(coap_ctx->buf)) {

    PRINTF("receiving UDP datagram from: ");
    PRINT6ADDR(&UIP_IP_BUF(coap_ctx->buf)->srcipaddr);
    PRINTF(":%u\n  Length: %u\n",
	   uip_ntohs(UIP_UDP_BUF(coap_ctx->buf)->srcport),
           uip_appdatalen(coap_ctx->buf));

    erbium_status_code =
    coap_parse_message(message, uip_appdata(coap_ctx->buf), uip_appdatalen(coap_ctx->buf));
    coap_set_context(message, coap_ctx);

    if(erbium_status_code == NO_ERROR) {

      NET_COAP_STAT(recv++);

      /*TODO duplicates suppression, if required by application */

      PRINTF("  Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version,
             message->type, message->token_len, message->code, message->mid);
      PRINTF("  URL[%d]: %.*s\n", message->uri_path_len, message->uri_path_len, message->uri_path);
      PRINTF("  Payload[%d]: %.*s\n", message->payload_len, message->payload_len, message->payload);

      /* handle requests */
      if(message->code >= COAP_GET && message->code <= COAP_DELETE) {

        /* use transaction buffer for response to confirmable request */
        if((transaction = coap_new_transaction(message->mid, coap_ctx,
                                               &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
                                               UIP_UDP_BUF(coap_ctx->buf)->srcport))) {
          uint32_t block_num = 0;
          uint16_t block_size = REST_MAX_CHUNK_SIZE;
          uint32_t block_offset = 0;
          int32_t new_offset = 0;

          /* prepare response */
          if(message->type == COAP_TYPE_CON) {
            /* reliable CON requests are answered with an ACK */
            coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
                              message->mid);
          } else {
            /* unreliable NON requests are answered with a NON as well */
            coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
                              coap_get_mid());
            /* mirror token */
          } if(message->token_len) {
            coap_set_token(response, message->token, message->token_len);
            /* get offset for blockwise transfers */
          }
          if(coap_get_header_block2
               (message, &block_num, NULL, &block_size, &block_offset)) {
            PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n",
                   (unsigned long)block_num, block_size, REST_MAX_CHUNK_SIZE, (unsigned long)block_offset);
            block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
            new_offset = block_offset;
          }

          /* invoke resource handler */
          if(service_cbk) {

            /* call REST framework and check if found and allowed */
            if(service_cbk
                 (message, response, transaction->packet + COAP_MAX_HEADER_SIZE,
                 block_size, &new_offset)) {

              if(erbium_status_code == NO_ERROR) {

                /* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */

                /* resource is unaware of Block1 */
                if(IS_OPTION(message, COAP_OPTION_BLOCK1)
                   && response->code < BAD_REQUEST_4_00
                   && !IS_OPTION(response, COAP_OPTION_BLOCK1)) {
                  PRINTF("Block1 NOT IMPLEMENTED\n");

                  erbium_status_code = NOT_IMPLEMENTED_5_01;
                  coap_error_message = "NoBlock1Support";

                  /* client requested Block2 transfer */
                } else if(IS_OPTION(message, COAP_OPTION_BLOCK2)) {

                  /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
                  if(new_offset == block_offset) {
                    PRINTF
                      ("Blockwise: unaware resource with payload length %u/%u\n",
                      response->payload_len, block_size);
                    if(block_offset >= response->payload_len) {
                      PRINTF
                        ("handle_incoming_data(): block_offset >= response->payload_len\n");

                      response->code = BAD_OPTION_4_02;
                      coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
                    } else {
                      coap_set_header_block2(response, block_num,
                                             response->payload_len -
                                             block_offset > block_size,
                                             block_size);
                      coap_set_payload(response,
                                       response->payload + block_offset,
                                       MIN(response->payload_len -
                                           block_offset, block_size));
                    } /* if(valid offset) */

                    /* resource provides chunk-wise data */
                  } else {
                    PRINTF("Blockwise: blockwise resource, new offset %ld\n",
                           (long)new_offset);
                    coap_set_header_block2(response, block_num,
                                           new_offset != -1
                                           || response->payload_len >
                                           block_size, block_size);

                    if(response->payload_len > block_size) {
                      coap_set_payload(response, response->payload,
                                       block_size);
                    }
                  } /* if(resource aware of blockwise) */

                  /* Resource requested Block2 transfer */
                } else if(new_offset != 0) {
                  PRINTF
                    ("Blockwise: no block option for blockwise resource, using block size %u\n",
                    COAP_MAX_BLOCK_SIZE);

                  coap_set_header_block2(response, 0, new_offset != -1,
                                         COAP_MAX_BLOCK_SIZE);
                  coap_set_payload(response, response->payload,
                                   MIN(response->payload_len,
                                       COAP_MAX_BLOCK_SIZE));
                } /* blockwise transfer handling */
              } /* no errors/hooks */
                /* successful service callback */
                /* serialize response */
            }
            if(erbium_status_code == NO_ERROR) {
              if((transaction->packet_len = coap_serialize_message(response,
                                                                   transaction->
                                                                   packet)) ==
                 0) {
                erbium_status_code = PACKET_SERIALIZATION_ERROR;
              }
            }
          } else {
            erbium_status_code = NOT_IMPLEMENTED_5_01;
            coap_error_message = "NoServiceCallbck"; /* no 'a' to fit into 16 bytes */
          } /* if(service callback) */
        } else {
          erbium_status_code = SERVICE_UNAVAILABLE_5_03;
          coap_error_message = "NoFreeTraBuffer";
        } /* if(transaction buffer) */

        /* handle responses */
      } else {

        if(message->type == COAP_TYPE_CON && message->code == 0) {
          PRINTF("Received Ping\n");
          erbium_status_code = PING_RESPONSE;
        } else if(message->type == COAP_TYPE_ACK) {
          /* transactions are closed through lookup below */
          PRINTF("Received ACK\n");
        } else if(message->type == COAP_TYPE_RST) {
          PRINTF("Received RST\n");
          /* cancel possible subscriptions */
          coap_remove_observer_by_mid(coap_ctx, &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
                                      UIP_UDP_BUF(coap_ctx->buf)->srcport, message->mid);
        }

        if((transaction = coap_get_transaction_by_mid(message->mid))) {
          /* free transaction memory before callback, as it may create a new transaction */
          restful_response_handler callback = transaction->callback;
          void *callback_data = transaction->callback_data;

          coap_clear_transaction(transaction);

          /* check if someone registered for the response */
          if(callback) {
            callback(callback_data, message);
          }
        }
        /* if(ACKed transaction) */
        transaction = NULL;

#if COAP_OBSERVE_CLIENT
	/* if observe notification */
        if((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON)
              && IS_OPTION(message, COAP_OPTION_OBSERVE)) {
          PRINTF("Observe [%u]\n", message->observe);
          coap_handle_notification(coap_ctx, &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
                                   UIP_UDP_BUF(coap_ctx->buf)->srcport, message);
        }
#endif /* COAP_OBSERVE_CLIENT */
      } /* request or response */
    } else { /* parsed correctly */
      NET_COAP_STAT(recv_err++);
    }

    /* if(parsed correctly) */
    if(erbium_status_code == NO_ERROR) {
      if(transaction) {
        coap_send_transaction(transaction);
      }
    } else if(erbium_status_code == MANUAL_RESPONSE) {
      PRINTF("Clearing transaction for manual response");
      coap_clear_transaction(transaction);
    } else {
      coap_message_type_t reply_type = COAP_TYPE_ACK;

      PRINTF("ERROR %u: %s\n", erbium_status_code, coap_error_message);
      coap_clear_transaction(transaction);

      if(erbium_status_code == PING_RESPONSE) {
        erbium_status_code = 0;
        reply_type = COAP_TYPE_RST;
      } else if(erbium_status_code >= 192) {
        /* set to sendable error code */
        erbium_status_code = INTERNAL_SERVER_ERROR_5_00;
        /* reuse input buffer for error message */
      }
      coap_init_message(message, reply_type, erbium_status_code,
                        message->mid);
      coap_set_payload(message, coap_error_message,
                       strlen(coap_error_message));
      coap_send_message(coap_ctx, &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
			UIP_UDP_BUF(coap_ctx->buf)->srcport,
                        uip_appdata(coap_ctx->buf),
			coap_serialize_message(message,
					       uip_appdata(coap_ctx->buf)));
    }
  }

  /* if(new data) */
  return erbium_status_code;
}