Skip to content

ensure docker-proxy sends UDP responses from correct source IP#2577

Closed
tsujamin wants to merge 6 commits intomoby:masterfrom
tsujamin:1729-docker-proxy-udp-srcaddr-fix
Closed

ensure docker-proxy sends UDP responses from correct source IP#2577
tsujamin wants to merge 6 commits intomoby:masterfrom
tsujamin:1729-docker-proxy-udp-srcaddr-fix

Conversation

@tsujamin
Copy link
Copy Markdown

@tsujamin tsujamin commented Sep 2, 2020

When docker-proxy listens for UDP connections on 0.0.0.0 or :: it is not garuanteed to respond to a packet from the same IP the packet was sent to. This affects machines running docker-proxy that are multi-homed (multiple default gateways), or have multiple IPs assigned to the same interface. The impact is that packets may not be routed correctly back to the client due to the change of source-ip.

Currently the only workaround is to explicitly bind docker-proxy to each local IP address (e.g. -p 1.1.1.1:1234/1234, -p 1.1.1.2:1234/1234), which is not possible in situations where IPs change (for instance, VPN tunnels coming up and down).

This PR uses the IP_PKTINFO socket option to retrieve the destination IP of the packet received by the host side, and ensures responses for the given connection are sent from the same IP. This should close #1729

I've added a basic unit test which simulate a multi-homed host, specifically:

  • EchoServer binds to 127.0.0.1
  • Proxy binds to 0.0.0.0 on the host side, so it will accept connections to any IP assigned to the host
  • A test connection to 127.0.0.2 (simulating a connection to a secondary IP address on a host) is initiated

Prior to this patch, docker-proxy would accept the packet on the 127.0.0.2 address, but respond from the 127.0.0.1 address. After the patch docker-proxy responds from the same IP the initial packet was sent to.

This is my first contribution to libnetwork/moby/docker, and I'm not a Go developer, so I appreciate any feedback or changes that may be required, and am happy to discuss/further advocate for this change.

@tsujamin tsujamin force-pushed the 1729-docker-proxy-udp-srcaddr-fix branch 2 times, most recently from fdbc3f3 to 6b5d57b Compare September 2, 2020 10:51
@thaJeztah
Copy link
Copy Markdown
Member

@arkodg @euanh PTAL

@tsujamin
Copy link
Copy Markdown
Author

tsujamin commented Oct 9, 2020

Anything additional changes/justification needed my end to progress this PR?

@thaJeztah
Copy link
Copy Markdown
Member

@arkodg @cpuguy83 PTAL

@tsujamin
Copy link
Copy Markdown
Author

Any issues?

Comment thread cmd/proxy/udp_proxy.go Outdated
var oob []byte //= nil

if srcAddr.To4() != nil {
cm := new(ipv4.ControlMessage)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems expensive here. Can we re-use control messages?
Even then we'll be allocating for Marshal which is also not great.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm parsing the go documentation correctly (which I'm likely not 😅 it's a normal language for me) I think cm is both read and written by WriteMsgUDP, so reusing it mightn't be appropriate as the state of the other cm members might have been mutated.

Is there anything else in the change I could modify to get it across the line performance wise? I appreciate the increased cost to processing UDP clients but it seem necessary for UDP correctness.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cm is only being used to marshal a message nothing shared between uses from what I can tell.
Each call to Marshal() is a new byte slice.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I’m on the same page now - working through a fix and addressing regressions now

@tsujamin tsujamin force-pushed the 1729-docker-proxy-udp-srcaddr-fix branch from 6b5d57b to 1fef916 Compare March 24, 2021 00:08
Signed-off-by: Benjamin George Roberts <[email protected]>
Signed-off-by: Benjamin George Roberts <[email protected]>
Signed-off-by: Benjamin George Roberts <[email protected]>
@tsujamin tsujamin force-pushed the 1729-docker-proxy-udp-srcaddr-fix branch from 1fef916 to 95e7148 Compare March 24, 2021 00:09
Signed-off-by: Benjamin George Roberts <[email protected]>
@tsujamin tsujamin force-pushed the 1729-docker-proxy-udp-srcaddr-fix branch from 2995e99 to 0881a04 Compare March 26, 2021 03:52
@tsujamin
Copy link
Copy Markdown
Author

@cpuguy83 I've removed the marshal and allocation from the reply loop (there's copy remaining but hopefully this will be lower impact performance wise) and tests are passing again. Want to take another look when you get a chance?

@tsujamin
Copy link
Copy Markdown
Author

@cpuguy83 any time to review this? the underlying issue hit us hard in prod today 😢

@tsujamin
Copy link
Copy Markdown
Author

tsujamin commented May 2, 2021

@arkodg @euanh @cpuguy83 @theJeztah any unresolved issues in the PR as stands?

Comment thread cmd/proxy/udp_proxy.go
if proxy.inet6Socket {
oobBuf = ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface)
} else {
oobBuf = ipv4.NewControlMessage(ipv4.FlagDst)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we have the same issue here as we did on Write where we allocate a new buffer for every read even though the buffere size should be constant.

Comment thread cmd/proxy/udp_proxy.go
var to *net.IP = nil

if from.IP.To4() == nil {
cm := new(ipv6.ControlMessage)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, can we re-use these objects instead of creating new ones on each read?

@cpuguy83
Copy link
Copy Markdown
Member

cpuguy83 commented May 4, 2021

Also I think we only need to incur this overhead when the IP is 0.0.0.0 (or ipv6 equiv)?

@cpuguy83
Copy link
Copy Markdown
Member

Note we have migrated this codebase over to github.com/moby/moby/libnetwork.
We are not accepting PR's on this repo anymore except for backports to be included in moby 20.10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docker-proxy does not set source IP correctly in UDP mode when bound on 0.0.0.0

3 participants