/*---------------------------------------------------------------------------*/ 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; }
/*---------------------------------------------------------------------------*/ 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; }
/* 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; }
/*---------------------------------------------------------------------------*/ 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; }