| /* |
| * Copyright (c) 2017 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <zephyr/types.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <misc/printk.h> |
| |
| #include <kernel.h> |
| |
| #include <net/buf.h> |
| #include <net/net_pkt.h> |
| #include <net/net_ip.h> |
| #include <net/udp.h> |
| |
| #include <tc_util.h> |
| |
| #include <net/coap.h> |
| |
| #define COAP_BUF_SIZE 128 |
| #define COAP_LIMITED_BUF_SIZE 13 |
| |
| #define NUM_PENDINGS 3 |
| #define NUM_OBSERVERS 3 |
| #define NUM_REPLIES 3 |
| |
| NET_PKT_TX_SLAB_DEFINE(coap_pkt_slab, 4); |
| |
| NET_BUF_POOL_DEFINE(coap_data_pool, 4, COAP_BUF_SIZE, 0, NULL); |
| |
| NET_BUF_POOL_DEFINE(coap_limited_data_pool, 4, COAP_LIMITED_BUF_SIZE, 0, NULL); |
| |
| static struct coap_pending pendings[NUM_PENDINGS]; |
| static struct coap_observer observers[NUM_OBSERVERS]; |
| static struct coap_reply replies[NUM_REPLIES]; |
| |
| /* This is exposed for this test in subsys/net/lib/coap/coap_link_format.c */ |
| bool _coap_match_path_uri(const char * const *path, |
| const char *uri, u16_t len); |
| |
| /* Some forward declarations */ |
| static void server_notify_callback(struct coap_resource *resource, |
| struct coap_observer *observer); |
| |
| static int server_resource_1_get(struct coap_resource *resource, |
| struct coap_packet *request); |
| |
| static const char * const server_resource_1_path[] = { "s", "1", NULL }; |
| static struct coap_resource server_resources[] = { |
| { .path = server_resource_1_path, |
| .get = server_resource_1_get, |
| .notify = server_notify_callback }, |
| { }, |
| }; |
| |
| #define MY_PORT 12345 |
| #define peer_addr { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ |
| 0, 0, 0, 0, 0, 0, 0, 0x2 } } } |
| static struct sockaddr_in6 dummy_addr = { |
| .sin6_family = AF_INET6, |
| .sin6_addr = peer_addr }; |
| |
| static int test_build_empty_pdu(void) |
| { |
| u8_t result_pdu[] = { 0x40, 0x01, 0x0, 0x0 }; |
| struct coap_packet cpkt; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| int result = TC_FAIL; |
| int r; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&cpkt, pkt, 1, COAP_TYPE_CON, |
| 0, NULL, COAP_METHOD_GET, 0); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| if (frag->len != sizeof(result_pdu)) { |
| TC_PRINT("Different size from the reference packet\n"); |
| goto done; |
| } |
| |
| if (memcmp(result_pdu, frag->data, frag->len)) { |
| TC_PRINT("Built packet doesn't match reference packet\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| static int test_build_simple_pdu(void) |
| { |
| u8_t result_pdu[] = { 0x55, 0xA5, 0x12, 0x34, 't', 'o', 'k', 'e', |
| 'n', 0xC1, 0x00, 0xFF, 'p', 'a', 'y', 'l', |
| 'o', 'a', 'd', 0x00 }; |
| struct coap_packet cpkt; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| const char token[] = "token"; |
| u8_t format = 0; |
| int result = TC_FAIL; |
| int r; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&cpkt, pkt, 1, COAP_TYPE_NON_CON, |
| strlen(token), (u8_t *)token, |
| COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED, |
| 0x1234); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| r = coap_packet_append_option(&cpkt, COAP_OPTION_CONTENT_FORMAT, |
| &format, sizeof(format)); |
| if (r) { |
| TC_PRINT("Could not append option\n"); |
| goto done; |
| } |
| |
| r = coap_packet_append_payload_marker(&cpkt); |
| if (r) { |
| TC_PRINT("Failed to set the payload marker\n"); |
| goto done; |
| } |
| |
| if (memcmp(result_pdu, frag->data, frag->len)) { |
| TC_PRINT("Built packet doesn't match reference packet\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_empty_pdu[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x0c, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| static int test_parse_empty_pdu(void) |
| { |
| u8_t pdu[] = { 0x40, 0x01, 0, 0 }; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct coap_packet cpkt; |
| u8_t ver, type, code; |
| u16_t id; |
| int result = TC_FAIL; |
| int r; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| net_pkt_append_all(pkt, sizeof(ipv6_empty_pdu), |
| (u8_t *)ipv6_empty_pdu, K_FOREVER); |
| net_pkt_append_all(pkt, sizeof(pdu), (u8_t *)pdu, K_FOREVER); |
| |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| memcpy(frag->data, pdu, sizeof(pdu)); |
| frag->len = NET_IPV6UDPH_LEN + sizeof(pdu); |
| |
| r = coap_packet_parse(&cpkt, pkt, NULL, 0); |
| if (r) { |
| TC_PRINT("Could not parse packet\n"); |
| goto done; |
| } |
| |
| ver = coap_header_get_version(&cpkt); |
| type = coap_header_get_type(&cpkt); |
| code = coap_header_get_code(&cpkt); |
| id = coap_header_get_id(&cpkt); |
| |
| if (ver != 1) { |
| TC_PRINT("Invalid version for parsed packet\n"); |
| goto done; |
| } |
| |
| if (type != COAP_TYPE_CON) { |
| TC_PRINT("Packet type doesn't match reference\n"); |
| goto done; |
| } |
| |
| if (code != COAP_METHOD_GET) { |
| TC_PRINT("Packet code doesn't match reference\n"); |
| goto done; |
| } |
| |
| if (id != 0) { |
| TC_PRINT("Packet id doesn't match reference\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_simple_pdu[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x15, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| static int test_parse_simple_pdu(void) |
| { |
| u8_t pdu[] = { 0x55, 0xA5, 0x12, 0x34, 't', 'o', 'k', 'e', |
| 'n', 0x00, 0xc1, 0x00, 0xff, 'p', 'a', 'y', |
| 'l', 'o', 'a', 'd', 0x00 }; |
| struct coap_packet cpkt; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct coap_option options[16]; |
| u8_t ver, type, code, tkl; |
| const u8_t token[8]; |
| u16_t id; |
| int result = TC_FAIL; |
| int r, count = 16; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| net_pkt_append_all(pkt, sizeof(ipv6_simple_pdu), |
| (u8_t *)ipv6_simple_pdu, K_FOREVER); |
| net_pkt_append_all(pkt, sizeof(pdu), (u8_t *)pdu, K_FOREVER); |
| |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| r = coap_packet_parse(&cpkt, pkt, NULL, 0); |
| if (r) { |
| TC_PRINT("Could not parse packet\n"); |
| goto done; |
| } |
| |
| ver = coap_header_get_version(&cpkt); |
| type = coap_header_get_type(&cpkt); |
| code = coap_header_get_code(&cpkt); |
| id = coap_header_get_id(&cpkt); |
| |
| if (ver != 1) { |
| TC_PRINT("Invalid version for parsed packet\n"); |
| goto done; |
| } |
| |
| if (type != COAP_TYPE_NON_CON) { |
| TC_PRINT("Packet type doesn't match reference\n"); |
| goto done; |
| } |
| |
| if (code != COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED) { |
| TC_PRINT("Packet code doesn't match reference\n"); |
| goto done; |
| } |
| |
| if (id != 0x1234) { |
| TC_PRINT("Packet id doesn't match reference\n"); |
| goto done; |
| } |
| |
| tkl = coap_header_get_token(&cpkt, (u8_t *)token); |
| |
| if (tkl != 5) { |
| TC_PRINT("Token length doesn't match reference\n"); |
| goto done; |
| } |
| |
| if (memcmp(token, "token", tkl)) { |
| TC_PRINT("Token value doesn't match the reference\n"); |
| goto done; |
| } |
| |
| count = coap_find_options(&cpkt, COAP_OPTION_CONTENT_FORMAT, |
| options, count); |
| if (count != 1) { |
| TC_PRINT("Unexpected number of options in the packet\n"); |
| goto done; |
| } |
| |
| if (options[0].len != 1) { |
| TC_PRINT("Option length doesn't match the reference\n"); |
| goto done; |
| } |
| |
| if (((u8_t *)options[0].value)[0] != 0) { |
| TC_PRINT("Option value doesn't match the reference\n"); |
| goto done; |
| } |
| |
| /* Not existent */ |
| count = coap_find_options(&cpkt, COAP_OPTION_ETAG, options, count); |
| if (count) { |
| TC_PRINT("There shouldn't be any ETAG option in the packet\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| static int test_retransmit_second_round(void) |
| { |
| struct coap_packet cpkt, resp; |
| struct coap_pending *pending, *resp_pending; |
| struct net_pkt *pkt, *resp_pkt = NULL; |
| struct net_buf *frag; |
| int result = TC_FAIL; |
| int r; |
| u16_t id; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| id = coap_next_id(); |
| |
| r = coap_packet_init(&cpkt, pkt, 1, COAP_TYPE_CON, 0, NULL, |
| COAP_METHOD_GET, id); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| pending = coap_pending_next_unused(pendings, NUM_PENDINGS); |
| if (!pending) { |
| TC_PRINT("No free pending\n"); |
| goto done; |
| } |
| |
| r = coap_pending_init(pending, &cpkt, (struct sockaddr *) &dummy_addr); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| /* We "send" the packet the first time here */ |
| if (!coap_pending_cycle(pending)) { |
| TC_PRINT("Pending expired too early\n"); |
| goto done; |
| } |
| |
| /* We simulate that the first transmission got lost */ |
| if (!coap_pending_cycle(pending)) { |
| TC_PRINT("Pending expired too early\n"); |
| goto done; |
| } |
| |
| resp_pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!resp_pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(resp_pkt, frag); |
| |
| r = coap_packet_init(&resp, resp_pkt, 1, COAP_TYPE_ACK, |
| 0, NULL, COAP_METHOD_GET, id); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| /* Now we get the ack from the remote side */ |
| resp_pending = coap_pending_received(&resp, pendings, NUM_PENDINGS); |
| if (pending != resp_pending) { |
| TC_PRINT("Invalid pending %p should be %p\n", |
| resp_pending, pending); |
| goto done; |
| } |
| |
| resp_pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); |
| if (resp_pending) { |
| TC_PRINT("There should be no active pendings\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| if (resp_pkt) { |
| net_pkt_unref(resp_pkt); |
| } |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| static void get_from_ip_addr(struct coap_packet *cpkt, |
| struct sockaddr_in6 *from) |
| { |
| struct net_udp_hdr hdr, *udp_hdr; |
| |
| udp_hdr = net_udp_get_hdr(cpkt->pkt, &hdr); |
| if (!udp_hdr) { |
| return; |
| } |
| |
| net_ipaddr_copy(&from->sin6_addr, &NET_IPV6_HDR(cpkt->pkt)->src); |
| from->sin6_port = udp_hdr->src_port; |
| from->sin6_family = AF_INET6; |
| } |
| |
| static bool ipaddr_cmp(const struct sockaddr *a, const struct sockaddr *b) |
| { |
| if (a->sa_family != b->sa_family) { |
| return false; |
| } |
| |
| if (a->sa_family == AF_INET6) { |
| return net_ipv6_addr_cmp(&net_sin6(a)->sin6_addr, |
| &net_sin6(b)->sin6_addr); |
| } else if (a->sa_family == AF_INET) { |
| return net_ipv4_addr_cmp(&net_sin(a)->sin_addr, |
| &net_sin(b)->sin_addr); |
| } |
| |
| return false; |
| } |
| |
| static void server_notify_callback(struct coap_resource *resource, |
| struct coap_observer *observer) |
| { |
| if (!ipaddr_cmp(&observer->addr, |
| (const struct sockaddr *) &dummy_addr)) { |
| TC_ERROR("The address of the observer doesn't match.\n"); |
| return; |
| } |
| |
| coap_remove_observer(resource, observer); |
| |
| TC_PRINT("You should see this\n"); |
| } |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_resource1[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x16, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x16, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| static int server_resource_1_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct coap_packet response; |
| struct coap_observer *observer; |
| struct sockaddr_in6 from; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| char payload[] = "This is the payload"; |
| u8_t token[8]; |
| u8_t tkl; |
| u16_t id; |
| int r; |
| |
| if (!coap_request_is_observe(request)) { |
| TC_PRINT("The request should enable observing\n"); |
| return -EINVAL; |
| } |
| |
| observer = coap_observer_next_unused(observers, NUM_OBSERVERS); |
| if (!observer) { |
| TC_PRINT("There should be an available observer.\n"); |
| return -EINVAL; |
| } |
| |
| tkl = coap_header_get_token(request, (u8_t *) token); |
| id = coap_header_get_id(request); |
| get_from_ip_addr(request, &from); |
| |
| coap_observer_init(observer, request, |
| (const struct sockaddr *)&from); |
| |
| coap_register_observer(resource, observer); |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| return -ENOMEM; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| return -ENOMEM; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| net_pkt_append_all(pkt, sizeof(ipv6_resource1), |
| (u8_t *)ipv6_resource1, K_FOREVER); |
| |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, tkl, token, |
| COAP_RESPONSE_CODE_OK, id); |
| if (r < 0) { |
| TC_PRINT("Unable to initialize packet.\n"); |
| return -EINVAL; |
| } |
| |
| coap_append_option_int(&response, COAP_OPTION_OBSERVE, resource->age); |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| TC_PRINT("Failed to set the payload marker\n"); |
| return -EINVAL; |
| } |
| |
| if (!net_pkt_append_all(pkt, strlen(payload), (u8_t *) payload, |
| K_FOREVER)) { |
| TC_PRINT("Failed to append the payload\n"); |
| return -EINVAL; |
| } |
| |
| resource->user_data = pkt; |
| |
| return 0; |
| } |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_valid_req[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x16, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x16, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_not_found_req[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x16, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x16, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| static int test_observer_server(void) |
| { |
| u8_t valid_request_pdu[] = { |
| 0x45, 0x01, 0x12, 0x34, |
| 't', 'o', 'k', 'e', 'n', |
| 0x60, /* enable observe option */ |
| 0x51, 's', 0x01, '1', /* path */ |
| }; |
| u8_t not_found_request_pdu[] = { |
| 0x45, 0x01, 0x12, 0x34, |
| 't', 'o', 'k', 'e', 'n', |
| 0x60, /* enable observe option */ |
| 0x51, 's', 0x01, '2', /* path */ |
| }; |
| struct coap_packet req; |
| struct coap_option options[4]; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| u8_t opt_num = 4; |
| int result = TC_FAIL; |
| int r; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| net_pkt_append_all(pkt, sizeof(ipv6_valid_req), |
| (u8_t *)ipv6_valid_req, K_FOREVER); |
| net_pkt_append_all(pkt, sizeof(valid_request_pdu), |
| (u8_t *)valid_request_pdu, K_FOREVER); |
| |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| r = coap_packet_parse(&req, pkt, options, opt_num); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| r = coap_handle_request(&req, server_resources, options, opt_num); |
| if (r) { |
| TC_PRINT("Could not handle packet\n"); |
| goto done; |
| } |
| |
| /* Suppose some time passes */ |
| |
| r = coap_resource_notify(&server_resources[0]); |
| if (r) { |
| TC_PRINT("Could not notify resource\n"); |
| goto done; |
| } |
| |
| net_pkt_unref(pkt); |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| net_pkt_append_all(pkt, sizeof(ipv6_not_found_req), |
| (u8_t *)ipv6_not_found_req, K_FOREVER); |
| net_pkt_append_all(pkt, sizeof(not_found_request_pdu), |
| (u8_t *)not_found_request_pdu, K_FOREVER); |
| |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| r = coap_packet_parse(&req, pkt, options, opt_num); |
| if (r) { |
| TC_PRINT("Could not initialize packet\n"); |
| goto done; |
| } |
| |
| r = coap_handle_request(&req, server_resources, options, opt_num); |
| if (r != -ENOENT) { |
| TC_PRINT("There should be no handler for this resource\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_obs_client[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x15, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| static int resource_reply_cb(const struct coap_packet *response, |
| struct coap_reply *reply, |
| const struct sockaddr *from) |
| { |
| TC_PRINT("You should see this\n"); |
| |
| return 0; |
| } |
| |
| static int test_observer_client(void) |
| { |
| struct coap_packet req, rsp; |
| struct coap_reply *reply; |
| struct coap_option options[4]; |
| struct net_pkt *pkt, *rsp_pkt = NULL; |
| struct net_buf *frag; |
| const char token[] = "token"; |
| const char * const *p; |
| u8_t opt_num = 4; |
| int observe = 0; |
| int result = TC_FAIL; |
| int r; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| if (!net_pkt_append_all(pkt, sizeof(ipv6_obs_client), |
| (u8_t *)ipv6_obs_client, K_FOREVER)) { |
| TC_PRINT("Unable to append IPv6 header\n"); |
| goto done; |
| } |
| |
| r = coap_packet_init(&req, pkt, 1, COAP_TYPE_CON, |
| strlen(token), (u8_t *)token, |
| COAP_METHOD_GET, coap_next_id()); |
| if (r < 0) { |
| TC_PRINT("Unable to initialize request\n"); |
| goto done; |
| } |
| |
| req.offset = sizeof(ipv6_obs_client); |
| |
| /* Enable observing the resource. */ |
| r = coap_append_option_int(&req, COAP_OPTION_OBSERVE, observe); |
| if (r < 0) { |
| TC_PRINT("Unable to add option to request int.\n"); |
| goto done; |
| } |
| |
| for (p = server_resource_1_path; p && *p; p++) { |
| r = coap_packet_append_option(&req, COAP_OPTION_URI_PATH, |
| *p, strlen(*p)); |
| if (r < 0) { |
| TC_PRINT("Unable to add option to request.\n"); |
| goto done; |
| } |
| } |
| |
| reply = coap_reply_next_unused(replies, NUM_REPLIES); |
| if (!reply) { |
| printk("No resources for waiting for replies.\n"); |
| goto done; |
| } |
| |
| coap_reply_init(reply, &req); |
| reply->reply = resource_reply_cb; |
| |
| /* Server side, not interesting for this test */ |
| r = coap_packet_parse(&req, pkt, options, opt_num); |
| if (r) { |
| TC_PRINT("Could not parse req packet\n"); |
| goto done; |
| } |
| |
| r = coap_handle_request(&req, server_resources, options, opt_num); |
| if (r) { |
| TC_PRINT("Could not handle packet\n"); |
| goto done; |
| } |
| |
| /* We cheat, and communicate using the resource's user_data */ |
| rsp_pkt = server_resources[0].user_data; |
| |
| /* 'rsp_pkt' contains the response now */ |
| |
| r = coap_packet_parse(&rsp, rsp_pkt, options, opt_num); |
| if (r) { |
| TC_PRINT("Could not parse rsp packet\n"); |
| goto done; |
| } |
| |
| reply = coap_response_received(&rsp, |
| (const struct sockaddr *) &dummy_addr, |
| replies, NUM_REPLIES); |
| if (!reply) { |
| TC_PRINT("Couldn't find a matching waiting reply\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| if (rsp_pkt) { |
| net_pkt_unref(rsp_pkt); |
| } |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| /* IPv6 + UDP frame (48 bytes) */ |
| static const unsigned char ipv6_block[] = { |
| /* IPv6 header starts here */ |
| 0x60, 0x00, 0x00, 0x00, 0x00, 0x17, 0x11, 0xFF, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |
| 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |
| /* UDP */ |
| 0xd8, 0xb4, 0x16, 0x33, 0x00, 0x17, 0x00, 0x00, |
| /* CoAP */ |
| }; |
| |
| static int test_block_size(void) |
| { |
| struct coap_block_context req_ctx, rsp_ctx; |
| struct coap_packet req; |
| struct net_pkt *pkt = NULL; |
| struct net_buf *frag; |
| const char token[] = "token"; |
| u8_t payload = 0xFE; |
| int result = TC_FAIL; |
| int r; |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| if (!net_pkt_append_all(pkt, sizeof(ipv6_block), |
| (u8_t *)ipv6_block, K_FOREVER)) { |
| TC_PRINT("Unable to append IPv6 header\n"); |
| goto done; |
| } |
| |
| r = coap_packet_init(&req, pkt, 1, COAP_TYPE_CON, |
| strlen(token), (u8_t *) token, |
| COAP_METHOD_POST, coap_next_id()); |
| if (r < 0) { |
| TC_PRINT("Unable to initialize request\n"); |
| goto done; |
| } |
| |
| req.offset = sizeof(ipv6_block); |
| |
| coap_block_transfer_init(&req_ctx, COAP_BLOCK_32, 127); |
| |
| r = coap_append_block1_option(&req, &req_ctx); |
| if (r) { |
| TC_PRINT("Unable to append block1 option\n"); |
| goto done; |
| } |
| |
| r = coap_append_size1_option(&req, &req_ctx); |
| if (r) { |
| TC_PRINT("Unable to append size1 option\n"); |
| goto done; |
| } |
| |
| r = coap_packet_append_payload_marker(&req); |
| if (r) { |
| TC_PRINT("Unable to append payload marker\n"); |
| goto done; |
| } |
| |
| r = coap_packet_append_payload(&req, (u8_t *)&payload, |
| coap_block_size_to_bytes(COAP_BLOCK_32)); |
| if (r) { |
| TC_PRINT("Unable to append payload marker\n"); |
| goto done; |
| } |
| |
| coap_block_transfer_init(&rsp_ctx, COAP_BLOCK_1024, 0); |
| |
| r = coap_update_from_block(&req, &rsp_ctx); |
| if (r < 0) { |
| TC_PRINT("Couldn't parse Block options\n"); |
| goto done; |
| } |
| |
| if (rsp_ctx.block_size != COAP_BLOCK_32) { |
| TC_PRINT("Couldn't get block size from request\n"); |
| goto done; |
| } |
| |
| if (rsp_ctx.current != 0) { |
| TC_PRINT("Couldn't get the current block size position\n"); |
| goto done; |
| } |
| |
| if (rsp_ctx.total_size != 127) { |
| TC_PRINT("Couldn't packet total size from request\n"); |
| goto done; |
| } |
| |
| /* Suppose that pkt was sent */ |
| net_pkt_unref(pkt); |
| |
| /* Let's try the second packet */ |
| coap_next_block(&req, &req_ctx); |
| |
| pkt = net_pkt_get_reserve(&coap_pkt_slab, 0, K_NO_WAIT); |
| if (!pkt) { |
| TC_PRINT("Could not get packet from pool\n"); |
| goto done; |
| } |
| |
| frag = net_buf_alloc(&coap_data_pool, K_NO_WAIT); |
| if (!frag) { |
| TC_PRINT("Could not get buffer from pool\n"); |
| goto done; |
| } |
| |
| net_pkt_frag_add(pkt, frag); |
| net_pkt_set_ip_hdr_len(pkt, NET_IPV6H_LEN); |
| net_pkt_set_ipv6_ext_len(pkt, 0); |
| |
| if (!net_pkt_append_all(pkt, sizeof(ipv6_block), |
| (u8_t *)ipv6_block, K_FOREVER)) { |
| TC_PRINT("Unable to append IPv6 header\n"); |
| goto done; |
| } |
| |
| r = coap_packet_init(&req, pkt, 1, COAP_TYPE_CON, |
| strlen(token), (u8_t *) token, |
| COAP_METHOD_POST, coap_next_id()); |
| if (r < 0) { |
| TC_PRINT("Unable to initialize request\n"); |
| goto done; |
| } |
| |
| req.offset = sizeof(ipv6_block); |
| |
| r = coap_append_block1_option(&req, &req_ctx); |
| if (r) { |
| TC_PRINT("Unable to append block1 option\n"); |
| goto done; |
| } |
| |
| r = coap_packet_append_payload_marker(&req); |
| if (r) { |
| TC_PRINT("Unable to append payload marker\n"); |
| goto done; |
| } |
| |
| r = coap_packet_append_payload(&req, (u8_t *)&payload, |
| coap_block_size_to_bytes(COAP_BLOCK_32)); |
| if (r) { |
| TC_PRINT("Unable to append payload marker\n"); |
| goto done; |
| } |
| |
| r = coap_update_from_block(&req, &rsp_ctx); |
| if (r < 0) { |
| TC_PRINT("Couldn't parse Block options\n"); |
| goto done; |
| } |
| |
| if (rsp_ctx.block_size != COAP_BLOCK_32) { |
| TC_PRINT("Couldn't get block size from request\n"); |
| goto done; |
| } |
| |
| if (rsp_ctx.current != coap_block_size_to_bytes(COAP_BLOCK_32)) { |
| TC_PRINT("Couldn't get the current block size position\n"); |
| goto done; |
| } |
| |
| if (rsp_ctx.total_size != 127) { |
| TC_PRINT("Couldn't packet total size from request\n"); |
| goto done; |
| } |
| |
| result = TC_PASS; |
| |
| done: |
| net_pkt_unref(pkt); |
| |
| TC_END_RESULT(result); |
| |
| return result; |
| } |
| |
| static int test_match_path_uri(void) |
| { |
| int result = TC_FAIL; |
| const char * const resource_path[] = { |
| "s", |
| "1", |
| "foobar", |
| "foobar3a", |
| "foobar3", |
| "devnull", |
| NULL |
| }; |
| const char *uri; |
| |
| uri = "/k"; |
| if (_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| uri = "/s"; |
| if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| uri = "/foobar"; |
| if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| uri = "/foobar2"; |
| if (_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| uri = "/foobar*"; |
| if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| uri = "/foobar3*"; |
| if (!_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| uri = "/devnull*"; |
| if (_coap_match_path_uri(resource_path, uri, strlen(uri))) { |
| TC_PRINT("Matching %s failed\n", uri); |
| goto out; |
| } |
| |
| result = TC_PASS; |
| |
| out: |
| TC_END_RESULT(result); |
| |
| return result; |
| |
| } |
| |
| static const struct { |
| const char *name; |
| int (*func)(void); |
| } tests[] = { |
| { "Build empty PDU test", test_build_empty_pdu, }, |
| { "Build simple PDU test", test_build_simple_pdu, }, |
| { "Parse emtpy PDU test", test_parse_empty_pdu, }, |
| { "Parse simple PDU test", test_parse_simple_pdu, }, |
| { "Test retransmission", test_retransmit_second_round, }, |
| { "Test observer server", test_observer_server, }, |
| { "Test observer client", test_observer_client, }, |
| { "Test block sized transfer", test_block_size, }, |
| { "Test match path uri", test_match_path_uri, }, |
| }; |
| |
| int main(int argc, char *argv[]) |
| { |
| int count, pass, result; |
| |
| TC_START("Test Zoap CoAP PDU parsing and building"); |
| |
| for (count = 0, pass = 0; count < ARRAY_SIZE(tests); count++) { |
| if (tests[count].func() == TC_PASS) { |
| pass++; |
| } |
| } |
| |
| TC_PRINT("%d / %d tests passed\n", pass, count); |
| |
| result = pass == count ? TC_PASS : TC_FAIL; |
| |
| TC_END_REPORT(result); |
| |
| return 0; |
| } |