-
Notifications
You must be signed in to change notification settings - Fork 2k
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
gcoap: ensure response address is the same as request address #18026
Conversation
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.
ACK. The change is very un-scary and I trust the provided testing
Hm, doesn't changing the |
I'm confused - why is the source IP address not set by the network layer correctly? |
Because it's sending a message from an unbound listener to a peer w/o knowing it's a response.
|
Sounds like we need to be able to select the address to send from via am argument instead. I think it should be possible via the aux struct as well. But personally, I think the behaviour here is fine. IMO code that previously relied on a specific IP address being used for sending from an unbound socket is buggy. (But I have to grant that it is indeed difficult to write correct code for sending from an unbound socket when the sender address matters, though.) |
Does this matter? In my understanding https://www.ietf.org/rfc/rfc6724.txt describes how to select the source address for outgoing IPv6 packets - unless an upper layer protocol has other requirements. So, I don't really understand why CoAP should have special requirements here. |
It's not CoAP that has this requirement but the sock API. |
That smells like a design issue with sock then. Why should the transport layer API set the IP source address? |
Well, add a second address to the Ethernet interface
ping works
CoAP request fails
with this patch
|
A CoAP server really has to respond with the IP address and port it received the request on, except for request's received on multicast IP addresses, in which case the response must be send from a unicast address instead. Otherwise a client would be even more exposed to spoofing attacks than it already is with CoAP over plain UDP. |
It's not a fundamental issue with our sock API, it's a problem with every sock API -- and the solution is straightforward (and we have it, as does POSIX with its RCVPKTINFO). CoAP thinks in terms of requests and responses, which is something sockets don't do, they either connect/bind or send arbitrary unrelated messages. A different API like TAPS might do this better, but that's a long way until that gets established, used widely, and I don't even know if it really works for embedded. But be that as it is, I think this can be as easy as what Ben proposed, just by setting the address in aux after extending the aux handler in sock_udp_sendv_aux to use the local address from there. |
@chrysn, I'm not sure I'm familiar with Just to get this straight: a UDP client (in this case a CoAP client) sends a request from address A to a CoAP server listening at address X. The server application sends a response to X. In the current implementation IPv6 would perform the source address selection according to RFC 6724 and may end up to choose a different IP address (B) that is also configured for one of its interfaces because it produces a better match to X than A. Correct? |
As far as I remember RFC 6724 always determines an order. There are no equally good matches. And the order of assignment is completely irrelevant. Otherwise we have a bug in the implementation of this RFC. |
If you have |
I'm not sure I'm familiar with `RCVPKTINFO` in POSIX. Do you have a link?
https://datatracker.ietf.org/doc/html/draft-ietf-lwig-coap-06#section-5.1.1
has both further references and puts it in context of CoAP.
Just to get this straight: [...]. The server application sends a response to X.
Correct, that's what would happen -- but it's not what CoAP needs.
The important point here is "response"; CoAP thinks in terms of requests
and responses, and there using the same address is obvious. But the
socket stack thinks in terms of "received data from an address" and
"sending data to an address", without request/response semantics, and
thus doesn't care unless we use aux to make it.
|
Check https://datatracker.ietf.org/doc/html/rfc6724#section-5 but in most realistic use cases you won't have two differing host IDs for the same network (unless you're using privacy extensions, but then the selection should also be clear).
Well, the network layer does make a choice - just apparently the wrong one in some cases. |
Okay, thanks for the pointer @chrysn. I think I do now understand the problem and agree with the proposed solution. |
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.
see inline
Code looks good to me. Please squash! Have you tested this? Otherwise I'd test first and ACK afterwards. |
If a node has multiple addresses we must reply to a request with the same address on which the request was received.
Still works:
|
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.
ACK. Code looks good, trusting your testing.
18257: drivers/wdt: add periph_wdt_auto_start for early watchdog r=benpicco a=benpicco 19272: gcoap: Do not send responses from multicast addresses r=chrysn a=chrysn ### Contribution description Since #18026, CoAP requests to multicast addresses (eg. `ff02::1`) came back from that exact address, which Linux rightfully just drops. The fix uses the existing multicast check from #17978 (thanks `@benpicco` for making me write this as dedicated function, I just had to generalize it removing one struct layer), and foregoes setting the source address when responding to multicasts. ### Testing procedure * Run the gcoap example * Send a CoAP request to a multicast address RIOT listens to, eg. `./aiocoap-client coap://'[ff02::1%tapbr0]'/.well-known/core --non` Before, this got no response (while you see it arrive on wireshark). After, you get a correct response with two lines of note: ``` WARNING:coap:Sending request to multicast via unicast request method Response arrived from different address; base URI is coap://[fe80::3c63:beff:fe85:ca96%tapbr0]/.well-known/core ``` (The former is aiocoap telling us that we're not using the nonexistent multicast API so it's really more of an anycast, the latter is useful factual information). Co-authored-by: Benjamin Valentin <benjamin.valentin@ml-pa.com> Co-authored-by: chrysn <chrysn@fsfe.org>
19272: gcoap: Do not send responses from multicast addresses r=benpicco a=chrysn ### Contribution description Since #18026, CoAP requests to multicast addresses (eg. `ff02::1`) came back from that exact address, which Linux rightfully just drops. The fix uses the existing multicast check from #17978 (thanks `@benpicco` for making me write this as dedicated function, I just had to generalize it removing one struct layer), and foregoes setting the source address when responding to multicasts. ### Testing procedure * Run the gcoap example * Send a CoAP request to a multicast address RIOT listens to, eg. `./aiocoap-client coap://'[ff02::1%tapbr0]'/.well-known/core --non` Before, this got no response (while you see it arrive on wireshark). After, you get a correct response with two lines of note: ``` WARNING:coap:Sending request to multicast via unicast request method Response arrived from different address; base URI is coap://[fe80::3c63:beff:fe85:ca96%tapbr0]/.well-known/core ``` (The former is aiocoap telling us that we're not using the nonexistent multicast API so it's really more of an anycast, the latter is useful factual information). Co-authored-by: chrysn <chrysn@fsfe.org>
Contribution description
If a node has multiple addresses we must reply to a request with the same address on which the request was received.
Testing procedure
Run GCoAP on a node that has multiple addresses assigned to it's interface, or a second interface with a global address:
We should be able to reach the CoAP server with any of these addresses:
And on the other interface, from Linux
On
master
we get-EPROTO
fromsock_udp_recv_buf_aux()
because the response address does not match the request.Let's try adding a second address to the Ethernet interface:
(with
CFLAGS += -DCONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF=3
)Issues/PRs references