-
Notifications
You must be signed in to change notification settings - Fork 2.1k
gcoap: add simple forward-proxy #13790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Very cool, @cgundogan! This capability is really useful. I also agree this use case would benefit from support for separate ACKs. Initial comments below. I agree that it makes sense to contain the forward proxy in a separate submodule. I'd like to think through how best to support such a thing. For example, can we accommodate the submodule's need to send a message without the addition of a public |
Do you see much value in supporting the Proxy-Uri option? My impression of that option is that it was primarily added to accommodate cross-proxying to protocols with incompatible URI schemes. Everyone else pretty please just use the destructured URIs. (I don't have objections to it being there, but it should be optional, and in the sense of educating CoAP users, shouldn't be the first thing they see when they look for a RIOT proxy). The look-into-memos thing looks like a good workaround until we send separate ACKs. (That's on the description; comments on the code to follow.) |
Yes, I work for a company with a significant number of incompatible devices. I expect this mechanism will allow us to evolve toward use of CoAP by putting a compatible device in front of the incompatible ones.
By "destructured" do you mean use of the individual Proxy-Scheme and Uri-* options?
I am not an expert in the proxy world and would appreciate it if you would summarize your thoughts on priorities in an RFC issue. Also @cgundogan, can you share your motivation for this work? |
that's more or less for research purposes :) Regarding the forward-proxy: I actually was planning to also work on a reverse-proxy. This would make the CoAP clients unaware of the whole proxying process. I just wanted to have the forward-proxy out there to get an early feedback from you more knowledgeable coap experts. |
Fine with me -- but those good CoAP citizens should IMO produce easy to parse messages (which is is more a comment for #13637). Mind you that parsing URIs is something that often goes wrong.
Yes. It makes little difference difference on the wire, but allows proxies to operate without a URI parser. Especially the most constrained proxies (the term used there is "intermediaries") will probably not support Proxy-Uri.
I've done so in the unofficial CoAP FAQ -- just don't take it as a third party opinion, because most of that is things I've written. The items in there might eventually wind up in a corrections-and-clarifications document that has been discussed in the WG, but that'll take some time. |
83b41f8
to
39faacf
Compare
the last rebase removed the now superfluous commit of #13789 |
Thanks for going ahead with #13818. It's next on my list to review. |
sys/include/net/gcoap.h
Outdated
* @param[in] buf Buffer containing the PDU | ||
* @param[in] len Length of the buffer | ||
* @param[in] remote Destination for the packet | ||
* @param[in] report_memo Pointer to the memo used for sending the request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long is that pointer valid, especially when the callback is NULL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that memo lifetime can be very short. However, if the memo context pointer references the client endpoint, then we don't need gcoap_req_send_report()
. This is an example of the value of using the memo context pointer.
sys/include/net/nanocoap.h
Outdated
* @return -ENOENT if Proxy-Uri option not found | ||
* @return -EINVAL if Proxy-Uri option cannot be parsed | ||
*/ | ||
static inline ssize_t coap_get_proxy_uri(const coap_pkt_t *pkt, char **target) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably more a general nanocoap than this-patch-in-particular question: Do we really want a per-option convenience function?
static uint8_t proxy_req_buf[CONFIG_GCOAP_PDU_BUF_SIZE]; | ||
static uint8_t scratch[48]; | ||
|
||
static bool _parse_endpoint(sock_udp_ep_t *remote, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something I can easily see becoming public (or publicly accessible as "create a coap request to that URI"), especially after the discussion in the CLI client on having coap get coap://[fe80::99%2]/foo
, and because I have hopes that uri_parser_result can become populatable by CoRAL applications.
I'd move some of it into the URI parsing, but for reasons unrelated to this PR, so that's no objection for here but an item from #13827.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. @cgundogan and I have discussed use by the gcoap and wakaama examples. I also agree that generation of an endpoint is not CoAP specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a similar piece of code in #16705 which I can spin out as a public function. The only question I have is where to put it ^^. Doesn't seem to fit either well uri_parser
or sock_util
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could try to refactor this as soon as the DoC client leaves the draft status (:
(void) remote; /* this is the origin server */ | ||
|
||
/* forward the response packet as-is to the client */ | ||
gcoap_dispatch((uint8_t *)pdu->hdr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There will need to be some compensation for mismatching token lengths (see the other comment about the empty token).
return -EINVAL; | ||
} | ||
|
||
if (urip->query) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding Uri-Path and query will need to happen in the options copying step where that option number is reached; otherwise, coap_opt_add_opaque will fail when the client sends an ETag, for example.
uint8_t *value; | ||
for (int i = 0; i < client_pkt->options_len; i++) { | ||
ssize_t optlen = coap_opt_get_next(client_pkt, &opt, &value, !i); | ||
if (optlen >= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Encountering a non-safe-to-forward option needs to result in an error response here.
sys/include/net/gcoap.h
Outdated
* @param[in] remote Destination for the packet | ||
* @param[in] report_memo Pointer to the memo used for sending the request | ||
* @param[in] resp_handler Callback when response received, may be NULL | ||
* @param[in] context User defined context passed to the response handler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the memo is return, it might make sense not to have setting the context here in the first place (especially if any of the unions mentioned above is used).
FWIW, I am thinking this through. A proxy is kind of its own world yet also has to operate within the larger context of gcoap. With fundamental issues like this, I find it helps to think through the issues a few times to layer on a good understanding. One thing is for sure. This PR would benefit from an implementation of separate response handling for a confirmable request. It's an incremental step with the existing infrastructure, and on my short list to do. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if there is a more integrated way to implement this work within gcoap. Conceptually, a forwarding proxy is a specialized listener on the server side. Let's call it a proxy listener. When a message arrives with a Proxy-Uri option, the proxy listener forwards it to the origin server. And when a response arrives from the origin server, the proxy listener forwards it to the requesting client.
Could we define a proxy listener as an application listener (gcoap_listener_t)? This approach means we are developing an API for extending the concept of a listener that can be used by other kinds of proxies.
Think about the code currently at the top of _handle_req()
. What if in _find_resource(), we extend how a match is done. In the listener struct, add a function pointer -- request_matcher
. This extends how we use coap_match_path()
but also includes the coap_pkt_t
. By default a listener uses coap_match_path()
, as is done currently. But a proxy listener would implement a custom function to also check for a proxy option in the coap_pkt_t. If no proxy option, _find_resource()
will move on to the next listener. Also, we would have to add a gcoap_add_listener_pos()
which would allow insertion of the proxy first in the list of listeners.
One other request regardless of the listener idea above. I'd like to limit the common public API, and gcoap_dispatch()
and gcoap_find_req_memo()
are only needed in this proxy context. So how about a gcoap_proxy_...()
(or something like that) header for these functions. The implementation would just be minimal; mostly one liners to call the internal implementation. In this way, the current gcoap internal uses of sock_udp_send()
for example also could remain as they are rather than use gcoap_dispatch()
.
39faacf
to
86227c7
Compare
dc4d97d
to
d4794ef
Compare
@kb2ma @chrysn I have updated the integration to address both your comments: the proxy is now an application and requires almost no modifications to The only diff I had to add is here. I wish I could also get rid of the modified call to What's still missing is the implementation of Proxy-Scheme and the consistency check for non-safe-option when copying the options. If we can acknowledge the current way we are moving, then I can add those features (I am aiming to keep the diff as small as possible for review). |
e8eb403
to
1b470ef
Compare
1b470ef
to
6d0d7f0
Compare
Just for context: I was asking @cgundogan to rebase the PR to have it ready for some experiments of mine. AFAIK, there are still some building blocks missing, to get it in a mergeable state. Did I understood you correctly there, @cgundogan? Should we mark this PR WIP? |
6d0d7f0
to
4111493
Compare
What building blocks are missing here though? |
a48fe14
to
a7b8ddd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I basically tested this over the course of my DoC experiments extensively. I have one comment. Once that is addressed, I think we can merge this.
a7b8ddd
to
44be2b9
Compare
Apart from #13790 (comment), please squash. |
02c5519
to
466f94d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please squash.
As said, I ran extensive experiments with this proxy implementation (on 2022.01). API changes to gCoAP since 2022.01 were addressed and further should be cought by Murdock.
Extensions like Proxy-Scheme, reverse proxies, or DTLS support can be added as a follow-up.
ed4d697
to
9746c3a
Compare
Thanks @miri64 and others for this comprehensive review! Squashed the current state. |
🚀 |
Contribution description
This PR adds a simple forward-proxy to gcoap.
See #13637 for related info.
A forward proxy parses a
Proxy-Uri
option of incoming requests and constructs a new request with the properUri-Path
andUri-Query
options. This newly created request is then sent to an endpoint that is specified in theProxy-Uri
option of the initial request.The second method using the
Proxy-Scheme
option is not implemented.Currently, gcoap does not support standalone acknowledgements. Therefore, the forward proxy is not able to stop the retransmissions on the client side. Hence, the client and the proxy will both do retransmissions for confirmable requests. I re-used the
_find_req_memo()
function to check for open requests with the sametoken
andclient_endpoint
. The proxy ignores incoming requests, if the proxy is already doing retransmissions for them.Depends on #13789Depends on #14029Testing procedure
gcoap
exampleUSEMODULE+=GCOAP_FORWARD_PROXY
Issues/PRs references