This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

ZB ZCL Tunneling - no data send

Hi, this is my first post here, so please forgive if category/description is not complete.

I'm trying to do a quick proof of concept using ZB ZCL Tunneling. ZED is a client side that opens a tunnel and sends some data and ZC is a server side receiving that data and sends something back to the client. Just for testing I'm doing it with ZB_AF_HA_PROFILE_ID & ZB_ZCL_CLUSTER_ID_TUNNELING.

On ZED I'm trying to request tunnel in the following way:

static void tunneling_request_tunnel(zb_bufid_t bufid)
{
    zb_ret_t zb_err_code;
    zb_uint16_t short_addr = 0x0000;

    if (bufid)
    {
    	NRF_LOG_INFO("%s", __FUNCTION__);
    	ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL(bufid, 							\
    										 short_addr,						\
											 ZB_APS_ADDR_MODE_16_ENDP_PRESENT,	\
											 TUNNEL_CLI_ENDPOINT,				\
											 TUNNEL_CLI_ENDPOINT,				\
											 ZB_AF_HA_PROFILE_ID, 				\
											 ZB_ZCL_DISABLE_DEFAULT_RESPONSE,	\
											 NULL,								\
											 200, 								\
											 0xDEAD, 							\
											 ZB_FALSE,							\
											 16);
    }
    else
    {
        zb_err_code = zb_buf_get_out_delayed(tunneling_request_tunnel);
        ZB_ERROR_CHECK(zb_err_code);
    }
}

On ZC I have a tunnel endpoint handler:

zb_uint8_t tunnel_ep_handler(zb_bufid_t bufid)
{
	static zb_bool_t tunnel_opened = ZB_FALSE;
	zb_zcl_parsed_hdr_t * header = ZB_BUF_GET_PARAM(bufid, zb_zcl_parsed_hdr_t);

	if(ZB_ZCL_CLUSTER_ID_TUNNELING != header->cluster_id)
	{
		return ZB_FALSE;
	}

	if(ZB_AF_HA_PROFILE_ID != header->profile_id)
	{
		return ZB_FALSE;
	}

	switch(header->cmd_id)
	{
		case ZB_ZCL_TUNNELING_CLI_CMD_REQUEST_TUNNEL:
		{
			zb_zcl_tunneling_request_tunnel_t tunnel_req;
			zb_zcl_parse_status_t status;
			zb_bufid_t buf_resp = zb_buf_get_out();

			ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL(&tunnel_req, bufid, status);

			(void)status;

			if(ZB_TRUE == tunnel_opened)
			{
				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, 0x0, ZB_ZCL_TUNNELING_STATUS_NO_MORE_IDS, tunnel_req.max_incoming_transfer_size );
			}
			else
			{
				tunnel_opened = ZB_TRUE;
				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, tunnel_id, ZB_ZCL_TUNNELING_STATUS_SUCCESS, tunnel_req.max_incoming_transfer_size );
			}

		    return ZB_TRUE;
		}

		case ZB_ZCL_TUNNELING_CLI_CMD_TRANSFER_DATA:
		{
			zb_zcl_tunneling_transfer_data_payload_t data_payload;
			zb_zcl_parse_status_t status;
			zb_bufid_t buf_data = zb_buf_get_out();

			ZB_ZCL_TUNNELING_GET_TRANSFER_DATA(&data_payload, bufid, status);

		    NRF_LOG_INFO("Data received.");

			tunnel_out_buf[0] = 'O';
			tunnel_out_buf[1] = 'K';

			zb_zcl_tunneling_server_send_transfer_data(buf_data, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, NULL, tunnel_id, 2, tunnel_out_buf);

			(void)status;

		    return ZB_TRUE;
		}

		case ZB_ZCL_TUNNELING_CLI_CMD_CLOSE_TUNNEL:
		{
			zb_zcl_tunneling_close_tunnel_t tunnel_close;
			zb_zcl_parse_status_t status;

			ZB_ZCL_TUNNELING_GET_CLOSE_TUNNEL(&tunnel_close, bufid, status);

			(void)status;

			if(ZB_FALSE == tunnel_opened)
			{
				// TBD
			}
			else
			{
				tunnel_opened = ZB_FALSE;
			}

		    return ZB_TRUE;
		}

		default:
			break;
	}

	return ZB_FALSE;
}

On ZED I have a tunnel endpoint handler as well:

zb_uint8_t tunnel_ep_handler(zb_bufid_t bufid)
{
	zb_zcl_parsed_hdr_t * header = ZB_BUF_GET_PARAM(bufid, zb_zcl_parsed_hdr_t);

	(void)tunnel_opened;

	if(ZB_ZCL_CLUSTER_ID_TUNNELING != header->cluster_id)
	{
		return ZB_FALSE;
	}

	if(ZB_AF_HA_PROFILE_ID != header->profile_id)
	{
		return ZB_FALSE;
	}


    NRF_LOG_INFO("Tunnel Handler, cmd_id: %x", header->cmd_id);

	switch(header->cmd_id)
	{
		case ZB_ZCL_TUNNELING_SRV_CMD_REQUEST_TUNNEL_RESPONSE:
		{
			zb_zcl_tunneling_request_tunnel_response_t tunnel_req_resp;
			zb_zcl_parse_status_t status;

			ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL_RESPONSE(&tunnel_req_resp, bufid, status);

			(void)status;

			NRF_LOG_INFO("Tunnel status: %x", tunnel_req_resp.tunnel_status);

			if(ZB_ZCL_TUNNELING_STATUS_SUCCESS != tunnel_req_resp.tunnel_status)
			{
				return ZB_FALSE;
			}

			tunnel_id = tunnel_req_resp.tunnel_id;
			tunnel_opened = ZB_TRUE;

			tunnel_out_buf[0] = 'O';
			tunnel_out_buf[1] = 'K';

			zb_bufid_t buf_data = zb_buf_get_out();

			zb_zcl_tunneling_client_send_transfer_data(buf_data, TUNNEL_CLI_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, NULL, tunnel_id, 2, tunnel_out_buf);

		    return ZB_TRUE;
		}
		case ZB_ZCL_TUNNELING_SRV_CMD_TUNNEL_CLOSURE_NOTIFICATION:
		{
			tunnel_opened = ZB_FALSE;
			return ZB_TRUE;
		}
		default:
			break;
	}

	return ZB_FALSE;
}

On Wireshark I can see proper flow - ZED requests tunnel and ZC sends request tunnel response with status success. So I expect then ZED to call zb_zcl_tunneling_client_send_transfer_data and send data, but zb_zcl_tunneling_client_send_transfer_data returns with code -1.

By accident I found a workaround - ZC must first send request tunnel response with error status followed by the second one reponse with success - then I'm getting transfer data from client to server, but still server to client transfer data request (zb_zcl_tunneling_server_send_transfer_data) returns -1.

Could you please give me a hint what I'm doing wrong?

  • Hello,

    Can you please attach the sniffer trace when the zb_zcl_tunneling_client_send_transfer_data returns 1?

    I have not heard of/used the ZCL tunneling before, so I am not sure how it is supposed to work. When I have the sniffer trace, I can forward this to our Zigbee team.

    Best regards,

    Edvin

  • Hi, thanks for quick response. Here goes two log files from Wireshark.

    The first is when zb_zcl_tunneling_client_send_transfer_data returns -1 (server sends only one tunnel request response with success before transfer data request):

    		case ZB_ZCL_TUNNELING_CLI_CMD_REQUEST_TUNNEL:
    		{
    			zb_zcl_tunneling_request_tunnel_t tunnel_req;
    			zb_zcl_parse_status_t status;
    			zb_bufid_t buf_resp = zb_buf_get_out();
    
    			ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL(&tunnel_req, bufid, status);
    
    			(void)status;
    
    			if(ZB_TRUE == tunnel_opened)
    			{
    				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, 0x0, ZB_ZCL_TUNNELING_STATUS_NO_MORE_IDS, tunnel_req.max_incoming_transfer_size );
    			}
    			else
    			{
    				tunnel_opened = ZB_TRUE;
    				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, tunnel_id, ZB_ZCL_TUNNELING_STATUS_SUCCESS, tunnel_req.max_incoming_transfer_size );
    			}
    
    		    return ZB_TRUE;
    		}
    

    logs_ret_code_error.txt

    The second is when zb_zcl_tunneling_client_send_transfer_data 0 (server sends two tunnel request response one after another, first with no more tunnels available, second with success before transfer data request):

    		case ZB_ZCL_TUNNELING_CLI_CMD_REQUEST_TUNNEL:
    		{
    			zb_zcl_tunneling_request_tunnel_t tunnel_req;
    			zb_zcl_parse_status_t status;
    			zb_bufid_t buf_resp = zb_buf_get_out();
    			zb_bufid_t buf_resp_dummy = zb_buf_get_out();
    
    			ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL(&tunnel_req, bufid, status);
    
    			(void)status;
    
    			if(ZB_TRUE == tunnel_opened)
    			{
    				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, 0x0, ZB_ZCL_TUNNELING_STATUS_NO_MORE_IDS, tunnel_req.max_incoming_transfer_size );
    			}
    			else
    			{
    				tunnel_opened = ZB_TRUE;
    				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp_dummy, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, tunnel_id, ZB_ZCL_TUNNELING_STATUS_NO_MORE_IDS, tunnel_req.max_incoming_transfer_size );
    				ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(buf_resp, header->addr_data.common_data.source.u, header->addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT, TUNNEL_SRV_ENDPOINT, TUNNEL_SRV_ENDPOINT, ZB_AF_HA_PROFILE_ID, ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(), NULL, tunnel_id, ZB_ZCL_TUNNELING_STATUS_SUCCESS, tunnel_req.max_incoming_transfer_size );
    			}
    
    		    return ZB_TRUE;
    		}

    logs_ret_code_ok.txt

    When you try that workaround with two tunnel request responses both with success, then above mentioned trick doesn't do the job.

    Thank you for taking care of it. Tunneling is new to me as well.

    Cheers!

  • Do you use the Nordic 802.15.4 sniffer? If so, can you save the sniffer trace as the default file type (.pcapng) and upload it?

    Is this issue something I can reproduce using a couple of DKs? Or does it require some external HW? I didn't quite understand if this tunnel was through an internet server or not.

    BR,

    Edvin

  • Here goes logs in .pcapng format:
    logs_ret_code_error.pcapng logs_ret_code_ok.pcapng

    I'm using two DK boards for that + dongle for sniffing. It's tunneling between two Zigbee devices to send some proprietary number (in that example just sending 'O' & 'K' from client to server and back).

  • Is it possible to send the project as well for reproduction of the issue?

Related