| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #if 1 |
| #define SYS_LOG_DOMAIN "coap-server" |
| #define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG |
| #define NET_LOG_ENABLED 1 |
| #endif |
| |
| #include <errno.h> |
| #include <misc/printk.h> |
| |
| #include <zephyr.h> |
| |
| #include <misc/byteorder.h> |
| #include <misc/util.h> |
| #include <net/buf.h> |
| #include <net/net_pkt.h> |
| #include <net/net_ip.h> |
| #include <net/udp.h> |
| |
| #include <net/coap.h> |
| #include <net/coap_link_format.h> |
| |
| #define MY_COAP_PORT 5683 |
| |
| #define STACKSIZE 2000 |
| |
| /* FIXME */ |
| #define BLOCK_WISE_TRANSFER_SIZE_GET 2048 |
| |
| #define ALL_NODES_LOCAL_COAP_MCAST \ |
| { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfd } } } |
| |
| #define MY_IP6ADDR \ |
| { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } } |
| |
| #define NUM_OBSERVERS 3 |
| |
| #define NUM_PENDINGS 3 |
| |
| /* block option helper */ |
| #define GET_BLOCK_NUM(v) ((v) >> 4) |
| #define GET_BLOCK_SIZE(v) (((v) & 0x7)) |
| #define GET_MORE(v) (!!((v) & 0x08)) |
| |
| static struct net_context *context; |
| |
| static const u8_t plain_text_format; |
| |
| static struct coap_observer observers[NUM_OBSERVERS]; |
| |
| static struct coap_pending pendings[NUM_PENDINGS]; |
| |
| static struct net_context *context; |
| |
| static struct k_delayed_work observer_work; |
| |
| static int obs_counter; |
| |
| static struct coap_resource *resource_to_notify; |
| |
| struct k_delayed_work retransmit_work; |
| |
| static void strip_headers(struct net_pkt *pkt) |
| { |
| /* Get rid of IP + UDP/TCP header if it is there. The IP header |
| * will be put back just before sending the packet. |
| */ |
| if (net_pkt_appdatalen(pkt) > 0) { |
| int header_len; |
| |
| header_len = net_buf_frags_len(pkt->frags) - |
| net_pkt_appdatalen(pkt); |
| if (header_len > 0) { |
| net_buf_pull(pkt->frags, header_len); |
| } |
| } else { |
| net_pkt_set_appdatalen(pkt, net_buf_frags_len(pkt->frags)); |
| } |
| } |
| |
| 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 int well_known_core_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct coap_packet response; |
| struct sockaddr_in6 from; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| int r; |
| |
| NET_DBG(""); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_well_known_core_get(resource, request, &response, pkt); |
| if (r < 0) { |
| net_pkt_unref(response.pkt); |
| return r; |
| } |
| |
| get_from_ip_addr(request, &from); |
| r = net_context_sendto(response.pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| net_pkt_unref(response.pkt); |
| } |
| |
| return r; |
| } |
| |
| static int test_del(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t tkl, code, type; |
| u8_t token[8]; |
| u16_t id; |
| int r; |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (type == COAP_TYPE_CON) { |
| type = COAP_TYPE_ACK; |
| } else { |
| type = COAP_TYPE_NON_CON; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_DELETED, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int test_put(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t code, type, tkl; |
| u8_t token[8]; |
| u16_t id; |
| int r; |
| |
| /* TODO: Check for payload, empty payload is an error case. */ |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (type == COAP_TYPE_CON) { |
| type = COAP_TYPE_ACK; |
| } else { |
| type = COAP_TYPE_NON_CON; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CHANGED, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int test_post(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| static const char * const location_path[] = { "location1", |
| "location2", |
| "location3", |
| NULL }; |
| const char * const *p; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t code, type, tkl; |
| u8_t token[8]; |
| u16_t id; |
| int r; |
| |
| /* TODO: Check for payload, empty payload is an error case. */ |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (type == COAP_TYPE_CON) { |
| type = COAP_TYPE_ACK; |
| } else { |
| type = COAP_TYPE_NON_CON; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CREATED, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| for (p = location_path; *p; p++) { |
| r = coap_packet_append_option(&response, |
| COAP_OPTION_LOCATION_PATH, |
| *p, strlen(*p)); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int location_query_post(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| static const char *const location_query[] = { "first=1", |
| "second=2", |
| NULL }; |
| const char * const *p; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t code, type, tkl; |
| u8_t token[8]; |
| u16_t id; |
| int r; |
| |
| /* TODO: Check for payload, empty payload is an error case. */ |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (type == COAP_TYPE_CON) { |
| type = COAP_TYPE_ACK; |
| } else { |
| type = COAP_TYPE_NON_CON; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CREATED, id); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| for (p = location_query; *p; p++) { |
| r = coap_packet_append_option(&response, |
| COAP_OPTION_LOCATION_QUERY, |
| *p, strlen(*p)); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int piggyback_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t token[8]; |
| u8_t payload[40], code, type; |
| u16_t id; |
| u8_t tkl; |
| int r; |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (type == COAP_TYPE_CON) { |
| type = COAP_TYPE_ACK; |
| } else { |
| type = COAP_TYPE_NON_CON; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CONTENT, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, |
| &plain_text_format, |
| sizeof(plain_text_format)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| /* The response that coap-client expects */ |
| r = snprintk((char *) payload, sizeof(payload), |
| "Type: %u\nCode: %u\nMID: %u\n", type, code, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload(&response, (u8_t *)payload, |
| strlen(payload)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int query_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct coap_option options[4]; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t payload[40], code, type, tkl; |
| u8_t token[8]; |
| u16_t id; |
| int i, r; |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| r = coap_find_options(request, COAP_OPTION_URI_QUERY, options, 4); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("num queries: %d\n", r); |
| |
| for (i = 0; i < r; i++) { |
| char str[16]; |
| |
| if (options[i].len + 1 > sizeof(str)) { |
| NET_INFO("Unexpected length of query: " |
| "%d (expected %zu)\n", |
| options[i].len, sizeof(str)); |
| break; |
| } |
| |
| memcpy(str, options[i].value, options[i].len); |
| str[options[i].len] = '\0'; |
| |
| NET_INFO("query[%d]: %s\n", i + 1, str); |
| } |
| |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, |
| tkl, (u8_t *) token, |
| COAP_RESPONSE_CODE_CONTENT, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, |
| &plain_text_format, |
| sizeof(plain_text_format)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| /* The response that coap-client expects */ |
| r = snprintk((char *) payload, sizeof(payload), |
| "Type: %u\nCode: %u\nMID: %u\n", type, code, id); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload(&response, (u8_t *)payload, |
| strlen(payload)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int separate_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| struct coap_pending *pending; |
| u8_t payload[40], code, type, tkl; |
| u8_t token[8]; |
| u16_t id; |
| int r; |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| if (type == COAP_TYPE_NON_CON) { |
| goto done; |
| } |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, |
| tkl, (u8_t *)token, 0, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| done: |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (type == COAP_TYPE_CON) { |
| type = COAP_TYPE_CON; |
| } else { |
| type = COAP_TYPE_NON_CON; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CONTENT, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, |
| &plain_text_format, |
| sizeof(plain_text_format)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| /* The response that coap-client expects */ |
| r = snprintk((char *) payload, sizeof(payload), |
| "Type: %u\nCode: %u\nMID: %u\n", type, code, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload(&response, (u8_t *)payload, |
| strlen(payload)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| if (type == COAP_TYPE_CON) { |
| pending = coap_pending_next_unused(pendings, NUM_PENDINGS); |
| if (!pending) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_pending_init(pending, &response, |
| (const struct sockaddr *)&from); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| coap_pending_cycle(pending); |
| pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); |
| |
| k_delayed_work_submit(&retransmit_work, pending->timeout); |
| } |
| |
| /* setup appdatalen */ |
| strip_headers(pkt); |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int large_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| static struct coap_block_context ctx; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t token[8], code, type; |
| u8_t payload[64]; |
| u16_t size; |
| u16_t id; |
| u8_t tkl; |
| int r; |
| |
| if (ctx.total_size == 0) { |
| coap_block_transfer_init(&ctx, COAP_BLOCK_64, |
| BLOCK_WISE_TRANSFER_SIZE_GET); |
| } |
| |
| r = coap_update_from_block(request, &ctx); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, |
| tkl, (u8_t *) token, |
| COAP_RESPONSE_CODE_CONTENT, id); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, |
| &plain_text_format, |
| sizeof(plain_text_format)); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_append_block2_option(&response, &ctx); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| size = min(coap_block_size_to_bytes(ctx.block_size), |
| ctx.total_size - ctx.current); |
| |
| memset(payload, 'A', size); |
| |
| r = coap_packet_append_payload(&response, (u8_t *)payload, size); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_next_block(&response, &ctx); |
| if (!r) { |
| /* Will return 0 when it's the last block. */ |
| memset(&ctx, 0, sizeof(ctx)); |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int get_option_int(const struct coap_packet *pkt, u8_t opt) |
| { |
| struct coap_option option = {}; |
| int r; |
| |
| r = coap_find_options(pkt, opt, &option, 1); |
| if (r <= 0) { |
| return -ENOENT; |
| } |
| |
| return coap_option_value_to_int(&option); |
| } |
| |
| static int large_update_put(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| static struct coap_block_context ctx; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t token[8]; |
| u16_t len, id, offset; |
| u8_t code; |
| u8_t type; |
| u8_t tkl; |
| int r; |
| bool last_block; |
| |
| r = get_option_int(request, COAP_OPTION_BLOCK1); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| last_block = !GET_MORE(r); |
| |
| /* initialize block context upon the arrival of first block */ |
| if (!GET_BLOCK_NUM(r)) { |
| coap_block_transfer_init(&ctx, COAP_BLOCK_64, 0); |
| } |
| |
| r = coap_update_from_block(request, &ctx); |
| if (r < 0) { |
| NET_ERR("Invalid block size option from request"); |
| return -EINVAL; |
| } |
| |
| frag = coap_packet_get_payload(request, &offset, &len); |
| if (!last_block && frag == NULL && len == 0) { |
| NET_ERR("Packet without payload\n"); |
| return -EINVAL; |
| } |
| |
| NET_INFO("**************\n"); |
| NET_INFO("[ctx] current %u block_size %u total_size %u\n", |
| ctx.current, coap_block_size_to_bytes(ctx.block_size), |
| ctx.total_size); |
| NET_INFO("**************\n"); |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| /* Do something with the payload */ |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| if (!last_block) { |
| code = COAP_RESPONSE_CODE_CONTINUE; |
| } else { |
| code = COAP_RESPONSE_CODE_CHANGED; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, |
| tkl, (u8_t *) token, code, id); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_append_block1_option(&response, &ctx); |
| if (r < 0) { |
| NET_ERR("Could not add Block1 option to response"); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static int large_create_post(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| static struct coap_block_context ctx; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t token[8]; |
| u8_t code, type; |
| u16_t len, id, offset; |
| u8_t tkl; |
| int r; |
| bool last_block; |
| |
| r = get_option_int(request, COAP_OPTION_BLOCK1); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| last_block = !GET_MORE(r); |
| |
| /* initialize block context upon the arrival of first block */ |
| if (!GET_BLOCK_NUM(r)) { |
| coap_block_transfer_init(&ctx, COAP_BLOCK_32, 0); |
| } |
| |
| r = coap_update_from_block(request, &ctx); |
| if (r < 0) { |
| NET_ERR("Invalid block size option from request"); |
| return -EINVAL; |
| } |
| |
| frag = coap_packet_get_payload(request, &offset, &len); |
| if (!last_block && frag == NULL && len == 0) { |
| NET_ERR("Packet without payload\n"); |
| return -EINVAL; |
| } |
| |
| get_from_ip_addr(request, &from); |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| net_pkt_frag_add(pkt, frag); |
| |
| if (!last_block) { |
| code = COAP_RESPONSE_CODE_CONTINUE; |
| } else { |
| code = COAP_RESPONSE_CODE_CREATED; |
| } |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, |
| tkl, (u8_t *)token, code, id); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_append_block1_option(&response, &ctx); |
| if (r < 0) { |
| NET_ERR("Could not add Block1 option to response"); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static void update_counter(struct k_work *work) |
| { |
| obs_counter++; |
| |
| if (resource_to_notify) { |
| coap_resource_notify(resource_to_notify); |
| } |
| |
| k_delayed_work_submit(&observer_work, 5 * MSEC_PER_SEC); |
| } |
| |
| static int send_notification_packet(const struct sockaddr *addr, u16_t age, |
| socklen_t addrlen, u16_t id, |
| const u8_t *token, u8_t tkl, |
| bool is_response) |
| { |
| struct coap_packet response; |
| struct coap_pending *pending; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| char payload[14]; |
| u8_t type = COAP_TYPE_CON; |
| int r; |
| |
| if (is_response) { |
| type = COAP_TYPE_ACK; |
| } |
| |
| if (!is_response) { |
| id = coap_next_id(); |
| } |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&response, pkt, 1, type, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CONTENT, id); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| if (age >= 2) { |
| coap_append_option_int(&response, COAP_OPTION_OBSERVE, age); |
| } |
| |
| r = coap_packet_append_option(&response, COAP_OPTION_CONTENT_FORMAT, |
| &plain_text_format, |
| sizeof(plain_text_format)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| /* The response that coap-client expects */ |
| r = snprintk((char *) payload, sizeof(payload), |
| "Counter: %d\n", obs_counter); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload(&response, (u8_t *)payload, |
| strlen(payload)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| if (type == COAP_TYPE_CON) { |
| pending = coap_pending_next_unused(pendings, NUM_PENDINGS); |
| if (!pending) { |
| return -EINVAL; |
| } |
| |
| r = coap_pending_init(pending, &response, addr); |
| if (r) { |
| return -EINVAL; |
| } |
| |
| coap_pending_cycle(pending); |
| pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); |
| |
| k_delayed_work_submit(&retransmit_work, pending->timeout); |
| } |
| |
| /* setup appdatalen */ |
| strip_headers(pkt); |
| |
| return net_context_sendto(pkt, addr, addrlen, NULL, 0, NULL, NULL); |
| } |
| |
| static int obs_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| struct coap_observer *observer; |
| struct sockaddr_in6 from; |
| u8_t token[8]; |
| u8_t code, type; |
| u16_t id; |
| u8_t tkl; |
| bool observe = true; |
| |
| get_from_ip_addr(request, &from); |
| |
| if (!coap_request_is_observe(request)) { |
| observe = false; |
| goto done; |
| } |
| |
| observer = coap_observer_next_unused(observers, NUM_OBSERVERS); |
| if (!observer) { |
| return -ENOMEM; |
| } |
| |
| coap_observer_init(observer, request, |
| (const struct sockaddr *)&from); |
| |
| coap_register_observer(resource, observer); |
| |
| resource_to_notify = resource; |
| |
| done: |
| code = coap_header_get_code(request); |
| type = coap_header_get_type(request); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| NET_INFO("*******\n"); |
| NET_INFO("type: %u code %u id %u\n", type, code, id); |
| NET_INFO("*******\n"); |
| |
| return send_notification_packet((const struct sockaddr *)&from, |
| observe ? resource->age : 0, |
| sizeof(struct sockaddr_in6), id, |
| token, tkl, true); |
| } |
| |
| static void obs_notify(struct coap_resource *resource, |
| struct coap_observer *observer) |
| { |
| send_notification_packet(&observer->addr, resource->age, |
| sizeof(observer->addr), 0, |
| observer->token, observer->tkl, false); |
| } |
| |
| static int core_get(struct coap_resource *resource, |
| struct coap_packet *request) |
| { |
| static const char dummy_str[] = "Just a test\n"; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| struct sockaddr_in6 from; |
| struct coap_packet response; |
| u8_t tkl; |
| u8_t token[8]; |
| u16_t id; |
| int r; |
| |
| get_from_ip_addr(request, &from); |
| id = coap_header_get_id(request); |
| tkl = coap_header_get_token(request, token); |
| |
| pkt = net_pkt_get_tx(context, K_FOREVER); |
| frag = net_pkt_get_data(context, K_FOREVER); |
| |
| net_pkt_frag_add(pkt, frag); |
| |
| r = coap_packet_init(&response, pkt, 1, COAP_TYPE_ACK, |
| tkl, (u8_t *)token, |
| COAP_RESPONSE_CODE_CONTENT, id); |
| if (r < 0) { |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload_marker(&response); |
| if (r) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| r = coap_packet_append_payload(&response, (u8_t *)dummy_str, |
| sizeof(dummy_str)); |
| if (r < 0) { |
| net_pkt_unref(pkt); |
| return -EINVAL; |
| } |
| |
| return net_context_sendto(pkt, (const struct sockaddr *)&from, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| } |
| |
| static const char * const test_path[] = { "test", NULL }; |
| |
| static const char * const segments_path[] = { "seg1", "seg2", "seg3", NULL }; |
| |
| static const char * const query_path[] = { "query", NULL }; |
| |
| static const char * const separate_path[] = { "separate", NULL }; |
| |
| static const char * const large_path[] = { "large", NULL }; |
| |
| static const char * const location_query_path[] = { "location-query", NULL }; |
| |
| static const char * const large_update_path[] = { "large-update", NULL }; |
| |
| static const char * const large_create_path[] = { "large-create", NULL }; |
| |
| static const char * const obs_path[] = { "obs", NULL }; |
| |
| static const char * const core_1_path[] = { "core1", NULL }; |
| static const char * const core_1_attributes[] = { |
| "title=\"Core 1\"", |
| "rt=core1", |
| NULL }; |
| |
| static const char * const core_2_path[] = { "core2", NULL }; |
| static const char * const core_2_attributes[] = { |
| "title=\"Core 1\"", |
| "rt=core1", |
| NULL }; |
| |
| static struct coap_resource resources[] = { |
| { .get = piggyback_get, |
| .post = test_post, |
| .del = test_del, |
| .put = test_put, |
| .path = test_path |
| }, |
| { .get = piggyback_get, |
| .path = segments_path, |
| }, |
| { .get = query_get, |
| .path = query_path, |
| }, |
| { .get = separate_get, |
| .path = separate_path, |
| }, |
| { .path = large_path, |
| .get = large_get, |
| }, |
| { .path = location_query_path, |
| .post = location_query_post, |
| }, |
| { .path = large_update_path, |
| .put = large_update_put, |
| }, |
| { .path = large_create_path, |
| .post = large_create_post, |
| }, |
| { .path = obs_path, |
| .get = obs_get, |
| .notify = obs_notify, |
| }, |
| { .get = well_known_core_get, |
| .path = COAP_WELL_KNOWN_CORE_PATH, |
| }, |
| { .get = well_known_core_get, |
| .path = COAP_WELL_KNOWN_CORE_PATH, |
| }, |
| { .get = core_get, |
| .path = core_1_path, |
| .user_data = &((struct coap_core_metadata) { |
| .attributes = core_1_attributes, |
| }), |
| }, |
| { .get = core_get, |
| .path = core_2_path, |
| .user_data = &((struct coap_core_metadata) { |
| .attributes = core_2_attributes, |
| }), |
| }, |
| { }, |
| }; |
| |
| static struct coap_resource *find_resouce_by_observer( |
| struct coap_resource *resources, struct coap_observer *o) |
| { |
| struct coap_resource *r; |
| |
| for (r = resources; r && r->path; r++) { |
| sys_snode_t *node; |
| |
| SYS_SLIST_FOR_EACH_NODE(&r->observers, node) { |
| if (&o->list == node) { |
| return r; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void udp_receive(struct net_context *context, |
| struct net_pkt *pkt, |
| int status, |
| void *user_data) |
| { |
| struct coap_packet request; |
| struct coap_pending *pending; |
| struct sockaddr_in6 from; |
| struct coap_option options[16] = { 0 }; |
| u8_t opt_num = 16; |
| int r; |
| |
| r = coap_packet_parse(&request, pkt, options, opt_num); |
| if (r < 0) { |
| NET_ERR("Invalid data received (%d)\n", r); |
| net_pkt_unref(pkt); |
| return; |
| } |
| |
| get_from_ip_addr(&request, &from); |
| pending = coap_pending_received(&request, pendings, |
| NUM_PENDINGS); |
| if (pending) { |
| net_pkt_unref(pkt); |
| return; |
| } |
| |
| if (coap_header_get_type(&request) == COAP_TYPE_RESET) { |
| struct coap_resource *r; |
| struct coap_observer *o; |
| |
| o = coap_find_observer_by_addr(observers, NUM_OBSERVERS, |
| (struct sockaddr *)&from); |
| if (!o) { |
| goto not_found; |
| } |
| |
| r = find_resouce_by_observer(resources, o); |
| if (!r) { |
| goto not_found; |
| } |
| |
| coap_remove_observer(r, o); |
| } |
| |
| not_found: |
| r = coap_handle_request(&request, resources, options, opt_num); |
| if (r < 0) { |
| NET_ERR("No handler for such request (%d)\n", r); |
| } |
| |
| net_pkt_unref(pkt); |
| } |
| |
| static bool join_coap_multicast_group(void) |
| { |
| static struct in6_addr my_addr = MY_IP6ADDR; |
| static struct sockaddr_in6 mcast_addr = { |
| .sin6_family = AF_INET6, |
| .sin6_addr = ALL_NODES_LOCAL_COAP_MCAST, |
| .sin6_port = htons(MY_COAP_PORT) }; |
| struct net_if_mcast_addr *mcast; |
| struct net_if_addr *ifaddr; |
| struct net_if *iface; |
| |
| iface = net_if_get_default(); |
| if (!iface) { |
| NET_ERR("Could not get te default interface\n"); |
| return false; |
| } |
| |
| #if defined(CONFIG_NET_APP_SETTINGS) |
| if (net_addr_pton(AF_INET6, |
| CONFIG_NET_APP_MY_IPV6_ADDR, |
| &my_addr) < 0) { |
| NET_ERR("Invalid IPv6 address %s", |
| CONFIG_NET_APP_MY_IPV6_ADDR); |
| } |
| #endif |
| |
| ifaddr = net_if_ipv6_addr_add(iface, &my_addr, NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| NET_ERR("Could not add unicast address to interface"); |
| return false; |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| mcast = net_if_ipv6_maddr_add(iface, &mcast_addr.sin6_addr); |
| if (!mcast) { |
| NET_ERR("Could not add multicast address to interface\n"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void retransmit_request(struct k_work *work) |
| { |
| struct coap_pending *pending; |
| int r; |
| |
| pending = coap_pending_next_to_expire(pendings, NUM_PENDINGS); |
| if (!pending) { |
| return; |
| } |
| |
| /* ref to avoid being freed by sendto() */ |
| net_pkt_ref(pending->pkt); |
| /* drop IP + UDP headers */ |
| strip_headers(pending->pkt); |
| |
| r = net_context_sendto(pending->pkt, &pending->addr, |
| sizeof(struct sockaddr_in6), |
| NULL, 0, NULL, NULL); |
| if (r < 0) { |
| /* no error, keeps retry */ |
| net_pkt_unref(pending->pkt); |
| } |
| |
| if (!coap_pending_cycle(pending)) { |
| /* last retransmit, clear pending and unreference packet */ |
| coap_pending_clear(pending); |
| return; |
| } |
| |
| /* unref to balance ref made previously */ |
| net_pkt_unref(pending->pkt); |
| k_delayed_work_submit(&retransmit_work, pending->timeout); |
| } |
| |
| void main(void) |
| { |
| static struct sockaddr_in6 any_addr = { |
| .sin6_family = AF_INET6, |
| .sin6_addr = IN6ADDR_ANY_INIT, |
| .sin6_port = htons(MY_COAP_PORT) }; |
| int r; |
| |
| if (!join_coap_multicast_group()) { |
| NET_ERR("Could not join CoAP multicast group\n"); |
| return; |
| } |
| |
| r = net_context_get(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context); |
| if (r) { |
| NET_ERR("Could not get an UDP context\n"); |
| return; |
| } |
| |
| r = net_context_bind(context, (struct sockaddr *) &any_addr, |
| sizeof(any_addr)); |
| if (r) { |
| NET_ERR("Could not bind the context\n"); |
| return; |
| } |
| |
| k_delayed_work_init(&retransmit_work, retransmit_request); |
| |
| k_delayed_work_init(&observer_work, update_counter); |
| k_delayed_work_submit(&observer_work, 5 * MSEC_PER_SEC); |
| |
| r = net_context_recv(context, udp_receive, 0, NULL); |
| if (r) { |
| NET_ERR("Could not receive in the context\n"); |
| return; |
| } |
| } |