From 549a1e77fae589122335ab6bb22338a65f1ffca9 Mon Sep 17 00:00:00 2001 From: yuanjm Date: Thu, 19 Nov 2020 19:26:38 +0800 Subject: [PATCH] feat(coap): Add coap multicast client and server example --- examples/protocols/coap_client/README.md | 24 +++- .../main/coap_client_example_main.c | 68 +++++++---- .../protocols/coap_client/sdkconfig.defaults | 1 + examples/protocols/coap_server/README.md | 22 ++-- .../coap_server/main/Kconfig.projbuild | 13 ++ .../main/coap_server_example_main.c | 115 ++++++++++++++++-- .../protocols/coap_server/sdkconfig.defaults | 1 + 7 files changed, 204 insertions(+), 40 deletions(-) create mode 100644 examples/protocols/coap_server/main/Kconfig.projbuild diff --git a/examples/protocols/coap_client/README.md b/examples/protocols/coap_client/README.md index bc3159db..cb19af8e 100644 --- a/examples/protocols/coap_client/README.md +++ b/examples/protocols/coap_client/README.md @@ -68,11 +68,33 @@ published under EPL+EDL: http://www.eclipse.org/californium/ ... ``` +You can use example coap_server together with coap_client example. +First, make coap_server and coap_client in the same local network by connecting to the same Wifi. +Then, configure the coap_client uri to `coap://232.10.11.12/Espressif ` or `coap://[ff02::fc]/Espressif`. +The coap_server will listen to ipv4 multicast ip address 232.10.11.12 and ipv6 multicast address ff02::fc. +You will see the following log: + +``` +... +I (468) example_connect: Connecting to HUAWEI_888... +I (1658) wifi:state: 0 -> 2 (b0) +I (1668) wifi:state: 2 -> 3 (0) +I (1675) wifi:state: 3 -> 5 (10) +I (1701) wifi:connected with HUAWEI_888, aid = 2, channel 1, HT20, bssid = 34:29:12:43:c5:40 +I (3654) tcpip_adapter: sta ip: 192.168.3.3, mask: 255.255.255.0, gw: 192.168.3.1 +I (3658) example_connect: Connected to HUAWEI_888 +I (3662) example_connect: IPv4 address: 192.168.3.3 +I (3671) example_connect: IPv6 address: fe80:0000:0000:0000:860d:8eff:fe9d:cd90 +I (3684) CoAP_client: Resolve the IP address is IPV6, FF02::FC +Received: no data +... +``` + ## libcoap Documentation This can be found at https://libcoap.net/doc/reference/4.2.0/ ## Troubleshooting -* Please make sure Target Url includes valid `host`, optional `port`, optional `path`, and begins +* Please make sure Target Url includes valid `host` or `ip`, optional `port`, optional `path`, and begins with `coap://` or `coap+tcp://` for a coap server that supports TCP (not all do including coap+tcp://californium.eclipse.org). diff --git a/examples/protocols/coap_client/main/coap_client_example_main.c b/examples/protocols/coap_client/main/coap_client_example_main.c index 3d4f9f4c..32df7a86 100644 --- a/examples/protocols/coap_client/main/coap_client_example_main.c +++ b/examples/protocols/coap_client/main/coap_client_example_main.c @@ -28,6 +28,7 @@ #define COAP_DEFAULT_TIME_SEC 5 /* Set this to 9 to get verbose logging from within libcoap */ +/* If want to change log level num to open log, don't forget to enlarge coap_example_task size*/ #define COAP_LOGGING_LEVEL 0 /* The examples use uri "coap://californium.eclipse.org" that @@ -39,7 +40,7 @@ #define COAP_DEFAULT_DEMO_URI CONFIG_TARGET_DOMAIN_URI const static char *TAG = "CoAP_client"; - +static char addr_str[64] = {0}; static int resp_wait = 1; static coap_optlist_t *optlist = NULL; static int wait_ms; @@ -126,9 +127,8 @@ clean_up: static void coap_example_task(void *p) { - struct hostent *hp; - struct ip4_addr *ip4_addr; - + struct addrinfo *ainfo; + struct addrinfo hints; coap_address_t dst_addr, src_addr; static coap_uri_t uri; const char* server_uri = COAP_DEFAULT_DEMO_URI; @@ -165,26 +165,43 @@ static void coap_example_task(void *p) } memcpy(phostname, uri.host.s, uri.host.length); - hp = gethostbyname(phostname); - free(phostname); - - if (hp == NULL) { - ESP_LOGE(TAG, "DNS lookup failed"); - vTaskDelay(1000 / portTICK_PERIOD_MS); + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + if (getaddrinfo(phostname, NULL, &hints, &ainfo) != 0) { + ESP_LOGE(TAG, "getaddrinfo failed"); free(phostname); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } - - /* Code to print the resolved IP. - - Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ - ip4_addr = (struct ip4_addr *)hp->h_addr; - ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr)); + free(phostname); coap_address_init(&src_addr); - src_addr.addr.sin.sin_family = AF_INET; - src_addr.addr.sin.sin_port = htons(0); - src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + + if (ainfo->ai_family == AF_INET) { + struct sockaddr_in *p = (struct sockaddr_in *)ainfo->ai_addr; + inet_ntop(AF_INET, &p->sin_addr, addr_str, sizeof(addr_str)); + ESP_LOGI(TAG, "Resolve the IP address is IPV4, %s",addr_str); + src_addr.addr.sin.sin_family = AF_INET; + src_addr.addr.sin.sin_port = htons(0); + src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + } else if (ainfo->ai_family == AF_INET6) { + struct sockaddr_in6 *p = (struct sockaddr_in6 *)ainfo->ai_addr; + inet_ntop(AF_INET6, &p->sin6_addr, addr_str, sizeof(addr_str)); + ESP_LOGI(TAG, "Resolve the IP address is IPV6, %s",addr_str); + struct in6_addr if_inaddr = { 0 }; + struct ip6_addr if_ipaddr = { 0 }; + src_addr.addr.sin6.sin6_port = htons(0); + src_addr.addr.sin6.sin6_family = AF_INET6; + src_addr.size = sizeof(struct sockaddr_in6); + bzero(&src_addr.addr.sin6.sin6_addr.un, sizeof(src_addr.addr.sin6.sin6_addr.un)); + tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &if_ipaddr); + inet6_addr_from_ip6addr(&if_inaddr, &if_ipaddr); + src_addr.addr.sin6.sin6_addr = if_inaddr; + } else { + ESP_LOGI(TAG, "ai_family is error %d", ainfo->ai_family); + goto clean_up; + } if (uri.path.length) { buflen = BUFSIZE; @@ -223,9 +240,15 @@ static void coap_example_task(void *p) } coap_address_init(&dst_addr); - dst_addr.addr.sin.sin_family = AF_INET; - dst_addr.addr.sin.sin_port = htons(uri.port); - dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; + dst_addr.size = ainfo->ai_addrlen; + memcpy(&dst_addr.addr, ainfo->ai_addr, ainfo->ai_addrlen); + if (ainfo->ai_family == AF_INET6) { + dst_addr.addr.sin6.sin6_family = AF_INET6; + dst_addr.addr.sin6.sin6_port = htons(uri.port); + } else { + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(uri.port); + } session = coap_new_client_session(ctx, &src_addr, &dst_addr, uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP : @@ -272,6 +295,7 @@ clean_up: if (session) coap_session_release(session); if (ctx) coap_free_context(ctx); coap_cleanup(); + freeaddrinfo(ainfo); /* Only send the request off once */ break; } diff --git a/examples/protocols/coap_client/sdkconfig.defaults b/examples/protocols/coap_client/sdkconfig.defaults index ce4e909c..d7ca663d 100644 --- a/examples/protocols/coap_client/sdkconfig.defaults +++ b/examples/protocols/coap_client/sdkconfig.defaults @@ -1 +1,2 @@ CONFIG_ENABLE_COAP=y +CONFIG_EXAMPLE_CONNECT_IPV6=y diff --git a/examples/protocols/coap_server/README.md b/examples/protocols/coap_server/README.md index cf00279a..e37886d3 100644 --- a/examples/protocols/coap_server/README.md +++ b/examples/protocols/coap_server/README.md @@ -22,6 +22,8 @@ idf.py menuconfig * Set default serial port under Serial Flasher config * Set WiFi SSID under Example Configuration * Set WiFi Password under Example Configuration +* Set IPv4 multicast ip address for server to listen to +* Set IPv6 multicast ip address for server to listen to ### Build and Flash @@ -41,16 +43,16 @@ and the log is such as the following: ``` ... -I (332) wifi: mode : sta (30:ae:a4:04:1b:7c) -I (1672) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1 -I (1672) wifi: state: init -> auth (b0) -I (1682) wifi: state: auth -> assoc (0) -I (1692) wifi: state: assoc -> run (10) -I (1692) wifi: connected with huawei_cw, channel 11 -I (1692) wifi: pm start, type: 1 - -I (2622) event: sta ip: 192.168.3.84, mask: 255.255.255.0, gw: 192.168.3.1 -I (2622) CoAP_server: Connected to AP +I (1828) wifi:state: 0 -> 2 (b0) +I (1838) wifi:state: 2 -> 3 (0) +I (1849) wifi:state: 3 -> 5 (10) +I (1868) wifi:connected with HUAWEI_888, aid = 1, channel 1, HT20, bssid = 34:29:12:43:c5:40 +I (3829) tcpip_adapter: sta ip: 192.168.3.2, mask: 255.255.255.0, gw: 192.168.3.1 +I (3834) example_connect: Connected to HUAWEI_888 +I (3837) example_connect: IPv4 address: 192.168.3.2 +I (3846) example_connect: IPv6 address: fe80:0000:0000:0000:a6cf:12ff:fee8:733b +I (3864) CoAP_server: Configured IPV4 Multicast address 232.10.11.12 +I (3875) CoAP_server: Configured IPV6 Multicast address ff02::fc ... ``` diff --git a/examples/protocols/coap_server/main/Kconfig.projbuild b/examples/protocols/coap_server/main/Kconfig.projbuild new file mode 100644 index 00000000..866e81d7 --- /dev/null +++ b/examples/protocols/coap_server/main/Kconfig.projbuild @@ -0,0 +1,13 @@ +menu "Example Configuration" + config TARGET_MULTICAST_IPV4 + string "IPv4 multicast address" + default "232.10.11.12" + help + Target IP for the example to listen. + + config TARGET_MULTICAST_IPV6 + string "IPv6 multicast address" + default "ff02::fc" + help + Target IP for the example to listen. +endmenu diff --git a/examples/protocols/coap_server/main/coap_server_example_main.c b/examples/protocols/coap_server/main/coap_server_example_main.c index a7b6f085..b6853707 100644 --- a/examples/protocols/coap_server/main/coap_server_example_main.c +++ b/examples/protocols/coap_server/main/coap_server_example_main.c @@ -21,12 +21,17 @@ #include "nvs_flash.h" #include "protocol_examples_common.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include #include "coap.h" /* Set this to 9 to get verbose logging from within libcoap */ +/* If want to change log level num to open log, don't forget to enlarge coap_example_task size*/ #define COAP_LOGGING_LEVEL 0 - +const static char *TAG = "CoAP_server"; static char espressif_data[100]; static int espressif_data_len = 0; @@ -93,6 +98,85 @@ hnd_espressif_delete(coap_context_t *ctx, response->code = COAP_RESPONSE_CODE(202); } +static int coap_example_join_ipv6(int sock, char *group_name) +{ + struct ipv6_mreq mreq = {0}; + int netif_index = 0, err = 0; + + netif_index = tcpip_adapter_get_netif_index(TCPIP_ADAPTER_IF_STA); + if (netif_index < 0) { + ESP_LOGE(TAG, "Failed to get netif index"); + return ESP_FAIL; + } + err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &netif_index, sizeof(uint8_t)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno); + return ESP_FAIL; + } + err = inet6_aton(group_name, &mreq.ipv6mr_multiaddr); + if (err != 1) { + ESP_LOGE(TAG, "Configured IPV6 multicast address '%s' is invalid.", group_name); + return ESP_FAIL; + } + + if (!IN6_IS_ADDR_MULTICAST(&mreq.ipv6mr_multiaddr)) { + ESP_LOGW(TAG, "Configured IPV6 multicast address '%s' is not a valid multicast address. This will probably not work.", group_name); + } + mreq.ipv6mr_interface = (unsigned int)netif_index; + + err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(struct ipv6_mreq)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno); + return ESP_FAIL; + } + ESP_LOGI(TAG, "Configured IPV6 Multicast address %s", group_name); + return ESP_OK; +} + +static int coap_example_join_ipv4(int sock, char *group_name) +{ + struct ip_mreq imreq = { 0 }; + struct in_addr iaddr = { 0 }; + int err = 0; + // Configure source interface + + tcpip_adapter_ip_info_t ip_info = { 0 }; + err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get IP address info. Error 0x%x", err); + return ESP_FAIL; + } + inet_addr_from_ip4addr(&iaddr, &ip_info.ip); + + // Configure multicast address to listen to + err = inet_aton(group_name, &imreq.imr_multiaddr.s_addr); + if (err != 1) { + ESP_LOGE(TAG, "Configured IPV4 multicast address '%s' is invalid.", group_name); + return ESP_FAIL; + } + + if (!IP_MULTICAST(ntohl(imreq.imr_multiaddr.s_addr))) { + ESP_LOGW(TAG, "Configured IPV4 multicast address '%s' is not a valid multicast address. This will probably not work.", group_name); + } + + // Assign the IPv4 multicast source interface, via its IP + // (only necessary if this socket is IPV4 only) + err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iaddr, sizeof(struct in_addr)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IP_MULTICAST_IF. Error %d", errno); + return ESP_FAIL; + } + + err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Configured IPV4 Multicast address %s", inet_ntoa(imreq.imr_multiaddr.s_addr)); + return ESP_OK; +} + static void coap_example_thread(void *p) { coap_context_t *ctx = NULL; @@ -107,24 +191,41 @@ static void coap_example_thread(void *p) coap_endpoint_t *ep_tcp = NULL; unsigned wait_ms; + ctx = coap_new_context(NULL); + if (!ctx) { + continue; + } /* Prepare the CoAP server socket */ coap_address_init(&serv_addr); serv_addr.addr.sin.sin_family = AF_INET; serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; serv_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - - ctx = coap_new_context(NULL); - if (!ctx) { - continue; - } - ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); + ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); //Add IPv4 endpoint if (!ep_udp) { goto clean_up; } + coap_example_join_ipv4(ep_udp->sock.fd, CONFIG_TARGET_MULTICAST_IPV4); ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); if (!ep_tcp) { goto clean_up; } + + /* Prepare the CoAP server socket */ + coap_address_init(&serv_addr); + serv_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); + serv_addr.addr.sin6.sin6_family = AF_INET6; + serv_addr.size = sizeof(struct sockaddr_in6); + bzero(&serv_addr.addr.sin6.sin6_addr.un, sizeof(serv_addr.addr.sin6.sin6_addr.un)); + ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); //Add IPv6 endpoint + if (!ep_udp) { + goto clean_up; + } + coap_example_join_ipv6(ep_udp->sock.fd, CONFIG_TARGET_MULTICAST_IPV6); + ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); + if (!ep_tcp) { + goto clean_up; + } + resource = coap_resource_init(coap_make_str_const("Espressif"), 0); if (!resource) { goto clean_up; diff --git a/examples/protocols/coap_server/sdkconfig.defaults b/examples/protocols/coap_server/sdkconfig.defaults index ce4e909c..d7ca663d 100644 --- a/examples/protocols/coap_server/sdkconfig.defaults +++ b/examples/protocols/coap_server/sdkconfig.defaults @@ -1 +1,2 @@ CONFIG_ENABLE_COAP=y +CONFIG_EXAMPLE_CONNECT_IPV6=y