absmach / magistrala

Industrial IoT Messaging and Device Management Platform
https://www.abstractmachines.fr/magistrala.html
Apache License 2.0
2.47k stars 672 forks source link

CoAP URL Issues with NB-IoT Modems #556

Closed oliexe closed 5 years ago

oliexe commented 5 years ago

Please consider a possibility to modify the endpoint of the CoAP services. All of the low-power NB-IoT/LTE modems are controlled using a standard AT Commands set and it is impossible to supply the whole Mainflux service URL to the modems (Probably thanks to an absence of the DNS). Most of them accept only IP of the server and PORT of the service rendering the Mainflux CoAP adapter unusable to dedicated NB-IoT modems.

Eg.: As an example, the modem that we are using in our ultra low-power hardware (Quectel BC95) is configured to send UDP messages to the server via following commands:

AT+NCDP=192.53.100.53 //Sets the server address AT+NMGS=10, AA7232088D0320623399 //Sends the message to the server (Size, Message)

But as documented in latest AT Commands set (Quectel_BC95_AT_Commands_Manual_V1.9.pdf) only a raw IP / PORT of the server can be supplied. This is applicable to most of the modems that focus on power efficiency.

As CoAP is highly suitable for devices that have energy constraints - No TCP, No DNS etc. I suggest implementing a possibility to re-route the URL for the CoAP and move the authorization inside the body of the message.

I am always happy to assist with testing.

drasko commented 5 years ago

@olirehacek I have serveral questions:

drasko commented 5 years ago

BTW. I do not think that we leverage today on the opened connection pool, not forcing client to send token upon every request, but only upon connection.

@dusanb94 CoAP is statefull (conection protocol), meaning that unlike for HTTP, CoAP keeps UDP connection (socket) opened. We could have a local mapping (in Redis for example) of connectionID:deviceToken (because deviceToken is received upon connection establishmen), and from there on we do not need to force the device to send us token anymore.

Upon disconnection we should remove this mapping from the local KV store.

Same goes to MQTT.

oliexe commented 5 years ago

@olirehacek I have serveral questions:

  • What is the minimal CoAP URL length? Zero?
  • Would you suggest routing on NGINX, so that we can maybe have several URL ports and route them internaly to API endpoints?
  • Why does Authorization header have to be removed (indeed we can make a spec to concatenate it at the beginning of the binary message, but I would likle to understand why is this necessary).
  1. The only constraint put on the URL of the server (On most of the modems) is that is it has to be a valid IP address, nothing except the valid IP:PORT is accepted by the modem.

  2. Definitely, a valid option to route the traffic to the API endpoints internally.

  3. If we are going to route the traffic using the NGINX don't we lose the supplied in the URL ? That is why I suggested to move the token directly inside the binary message.

Best regards, Ondrej

drasko commented 5 years ago
  1. The only constraint put on the URL of the server (On most of the modems) is that is it has to be a valid IP address, nothing except the valid IP:PORT is accepted by the modem.

OK, so empty URL.

  1. Definitely, a valid option to route the traffic to the API endpoints internally.

This is what I thought. OK, nice.

  1. If we are going to route the traffic using the NGINX don't we lose the supplied in the URL ? That is why I suggested to move the token directly inside the binary message. No, we won't. In the case of UDP proxying NginX is playing the role of L3/L4 LB (https://www.nginx.com/resources/glossary/layer-4-load-balancing/), not even touching the data in the higher layers.

Hmmm... I was thinking that we are keeping auth token in the CoAP header, but it looks like it is in the Uri-query (https://tools.ietf.org/html/rfc7252#section-5.10.1). @dusanb94 can confirm this. (If I remember well - it could not fit into the header).

Effectively - we must move this token somewhere then. Question is where. Because - we can indeed concatenate it to the binary message, but what happens when you can not change the firmware of the device?

Is there any way for your modem to pass this info to the server (I must look at the spec of Quectel C95)?

oliexe commented 5 years ago

Just to add more info regarding this issue, this is a very nice article that describes connecting the Quectel modems to CoAP + LwM2M (Wakama): https://bearmetal.eu/theden/sending-oma-lwm2m-coap-messages-with-quectel-bc68/

oliexe commented 5 years ago
  1. The only constraint put on the URL of the server (On most of the modems) is that is it has to be a valid IP address, nothing except the valid IP:PORT is accepted by the modem.

OK, so empty URL.

  1. Definitely, a valid option to route the traffic to the API endpoints internally.

This is what I thought. OK, nice.

  1. If we are going to route the traffic using the NGINX don't we lose the supplied in the URL ? That is why I suggested to move the token directly inside the binary message. No, we won't. In the case of UDP proxying NginX is playing the role of L3/L4 LB (https://www.nginx.com/resources/glossary/layer-4-load-balancing/), not even touching the data in the higher layers.

Hmmm... I was thinking that we are keeping auth token in the CoAP header, but it looks like it is in the Uri-query (https://tools.ietf.org/html/rfc7252#section-5.10.1). @dusanb94 can confirm this. (If I remember well - it could not fit into the header).

Effectively - we must move this token somewhere then. Question is where. Because - we can indeed concatenate it to the binary message, but what happens when you can not change the firmware of the device?

Is there any way for your modem to pass this info to the server (I must look at the spec of Quectel C95)?

Regarding the Quectel and other NB-IoT modems that are stuck with AT Commands as only means of communication the only thing I can do is pretty much set the IP of the server and transmit raw UDP messages. There is some "Huawei IoT Platform" that is pretty much some sort of crippled LwM2M implementation inside the Modem firmware, it can be used with for example Wakama Server, but it is specifically tailored for use with the proprietary Huawei platform.

So I am trying to find a good way to transmit the Sensor Measurements, Sensor Metadata over to Mainflux while using just the Raw UDP messaging. The only idea I had was to concatenate the token directly inside the message.

Good way to get the idea of the available functionality is to check out: "Examples" , "UDP Commands" inside the "General" section and "Huawei IoT Platform Commands" section inside the : https://www.quectel.com/UploadImage/Downlad/Quectel_BC95_AT_Commands_Manual_V1.9.pdf

drasko commented 5 years ago

Token concatenation would work here. Mainflux tokens are of the fixed size, so prefixing them to the message should pose no problems.

As mentioned, I think that they are needed only on connection establishment, and we need to add this mapping into Mainflux in order to avoid sending tokens every time and preserve battery life.

oliexe commented 5 years ago

Any update on this issue ? It is extremly important for use so we can implement the NB-IoT sensors. Thanks ! :)

nmarcetic commented 5 years ago

@olirehacek It will be part of 0.8 release, planned for next week ;)

drasko commented 5 years ago

@olirehacek coming back to this Quctel modem AT commands - you should run CoAP client on CPU and let it send UDP payload. This way it will send messages that make sense to a protocol. And then this way you will be capable to let CoAP library format the UDP message correctly when you add token in Uri-Query.

@dusanb94 can you please explain us how CoAP token is sent today?

Similarly - it is true that you can configure only IP and Port on the modem via AT commands. However, CoAP URL will be a part of raw UDP message, and when you format your message correctly, then CoAP server (which is indeed UDP server) will know how to route.

So again in this case - use CoAP client library on your modem. If possible run a RTOS with CoAP client already present (like Zephyr, RIOT, FreeRTOS or Contiki) so that these libraries format you raw UDP message as it should be.

Otherwise you will have to format raw UDP message by hand. Maybe try running CoAP client on your PC and dump UDP traffic with Wireshark as you send messages to Mainflux CoAP server to figure out right content of raw UDP payload.

oliexe commented 5 years ago

@olirehacek coming back to this Quctel modem AT commands - you should run CoAP client on CPU and let it send UDP payload. This way it will send messages that make sense to a protocol. And then this way you will be capable to let CoAP library format the UDP message correctly when you add token in Uri-Query.

@dusanb94 can you please explain us how CoAP token is sent today?

Similarly - it is true that you can configure only IP and Port on the modem via AT commands. However, CoAP URL will be a part of raw UDP message, and when you format your message correctly, then CoAP server (which is indeed UDP server) will know how to route.

So again in this case - use CoAP client library on your modem. If possible run a RTOS with CoAP client already present (like Zephyr, RIOT, FreeRTOS or Contiki) so that these libraries format you raw UDP message as it should be.

Otherwise you will have to format raw UDP message by hand. Maybe try running CoAP client on your PC and dump UDP traffic with Wireshark as you send messages to Mainflux CoAP server to figure out right content of raw UDP payload.

I have FreeRTOS up and running on the CPU, will look into it today. Thanks

Edit: By the looks of it there is no FreeRTPS CoAP Client for STM32s, only ESP8266 (Where it is very easy to implement) but will try to work something out, maybe modify the ESP8266 code - will get back to you with some results later

drasko commented 5 years ago

Yes, without CoAP client library it will be difficult and not quite effective to implement all UDP packets by hand - CoAP client library does exactly that, it formats UDP packets so that they can be understood by the server.

drasko commented 5 years ago

BTW, maybe ot would be worh to try Zephyr RTOS - it should have a good support for STM32. It is Linux Foundation backed project and there is increasing activity and support around it.

Other alternative would be using mbed - I think it also has a CoAP + DTLS support.

oliexe commented 5 years ago

@drasko Managed to serialize the CoAP by modifying and porting the ESP implementation - not very difficult thing to do, thanks for the link. Far from solid, but we will get there.

Also, I was on the phone with Quectel and a new revision of the hardware (BC66 and BB95-G) modem will have a full implementation of CoAP ! So this issue is no longer valid and Mainflux should stick with the current implementation.

Thanks for all the work !

drasko commented 5 years ago

Fantastic news @olirehacek, you are advancing fast guys.

Note that one of the next steps will be to solve DTLS. You can ask Quctel guys about this also, especially that this is separate lib from CoAP. FreeRTOS can embed mbedTLS and you can find some examples again leveraging on ESP8266/ESP32 code (which uses FreeRTOS): https://github.com/SuperHouse/esp-open-rtos/tree/master/examples/aws_iot. Otherwise, you can look at some of my examples: https://github.com/mainflux/proxy/tree/master/examples

Never the less, DTLS on the server side will not be trivial, as Go does not have native DTLS support, and NginX which should terminate TLS connection in our case supports only experimental DTLS support.

In other work, we will have to solve https://github.com/mainflux/mainflux/issues/428 in order this to work, and we will really need your help there.