Skip to content

bpo-35545: Fix asyncio discarding IPv6 scopes #11271

Merged
miss-islington merged 4 commits into
python:masterfrom
lepaperwan:bpo-35545
May 17, 2019
Merged

bpo-35545: Fix asyncio discarding IPv6 scopes #11271
miss-islington merged 4 commits into
python:masterfrom
lepaperwan:bpo-35545

Conversation

@lepaperwan
Copy link
Copy Markdown
Contributor

@lepaperwan lepaperwan commented Dec 20, 2018

This PR proposes a solution to bpo-35545 by adding an optional flowinfo and scopeid to asyncio.base_events._ipaddr_info to carry the full address information into _ipaddr_info and avoid discarding IPv6 specific information.

Changelog entry & regression tests to come.

https://bugs.python.org/issue35545

@1st1
Copy link
Copy Markdown
Member

1st1 commented Feb 14, 2019

Please add a NEWS file.

@1st1
Copy link
Copy Markdown
Member

1st1 commented Feb 14, 2019

@asvetlov what do you think?

Copy link
Copy Markdown
Contributor

@asvetlov asvetlov left a comment

Choose a reason for hiding this comment

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

I think we need NEWs entry, everything is fine.

@asvetlov
Copy link
Copy Markdown
Contributor

@lepaperwan there is a possibility to merge the PR as is but if you add a simple regression test it would be awesome!

@asvetlov
Copy link
Copy Markdown
Contributor

Thanks for the PR.
I know asyncio is full of mock-based tests but we are working to get rid of them where is possible.
The reason is: real tests are much more stable on changing asyncio internals but mocked tests require fixes to reflect new objects structure/methods/signatures etc.

From my understanding, the test can create asyncio local server, connect to it and check the socket peer name or something like this.
Would you try this strategy?

@lepaperwan
Copy link
Copy Markdown
Contributor Author

lepaperwan commented May 14, 2019 via email

@lepaperwan
Copy link
Copy Markdown
Contributor Author

lepaperwan commented May 16, 2019

I hadn't thought this through the other night while answering so I'll try to make this as complete as possible.

Testing that the scope is properly kept through the internals of asyncio actually requires that the test host has a usable scoped IP address (which is unusual on the loopback interface).

Should it be the case, the test would have to either dynamically acquire that IP and use it to bind the server, or know it beforehand and have it hardcoded. Binding to an invalid address, of course, fails.

>>> import socket

>>> s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
>>> s.bind(("fe80::1", 80, 0, 1))

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 99] Cannot assign requested address

Another solution is to rely on scoped IPs failing to be used if the scope ID is not explicitly given.

>>> import socket

>>> s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
>>> s.connect(("fe80::1", 80))

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument

However if there is no server to answer, as expected, the call fails as well.

>>> import socket

>>> s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
>>> s.connect(("fe80::1", 80, 0, 1))

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ConnectionRefusedError: [Errno 111] Connection refused

And if that interface doesn't have a scoped address, it fails as well.

>>> import socket

>>> s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
>>> s.connect(("fe80::1", 80, 0, 1))

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 101] Network is unreachable

The Invalid argument method works with asyncio as well. (This is on 3.7.2)

>>> import asyncio

>>> asyncio.run(asyncio.open_connection("fe80::1%1", 80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.7/asyncio/streams.py", line 77, in open_connection
    lambda: protocol, host, port, **kwds)
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 959, in create_connection
    raise exceptions[0]
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 946, in create_connection
    await self.sock_connect(sock, address)
  File "/usr/local/lib/python3.7/asyncio/selector_events.py", line 464, in sock_connect
    return await fut
  File "/usr/local/lib/python3.7/asyncio/selector_events.py", line 469, in _sock_connect
    sock.connect(address)
OSError: [Errno 22] Invalid argument

Imposing additional constraints on the test host isn't ideal because while it might be possible to setup the CIs appropriately, it isn't desirable and contributors might not have identical setups.

I'm not certain what Python's test policy is at this stage. I'd assume expecting errors for anything other than error detection/handling isn't great, constraining the test host isn't great, and mocks aren't great either.

If anyone has ideas on how to best go about testing this please chime in :)

@asvetlov
Copy link
Copy Markdown
Contributor

Sorry, I've missed the fact that IPv6 scopes require specific host configuration.
Let's keep mocking here.

Thank you very much for your contribution!

@miss-islington
Copy link
Copy Markdown
Contributor

Thanks @lepaperwan for the PR 🌮🎉.. I'm working now to backport this PR to: 3.7.
🐍🍒⛏🤖 I'm not a witch! I'm not a witch!

@bedevere-bot
Copy link
Copy Markdown

GH-13379 is a backport of this pull request to the 3.7 branch.

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request May 17, 2019
This PR proposes a solution to [bpo-35545](https://bugs.python.org/issue35545) by adding an optional `flowinfo` and `scopeid` to `asyncio.base_events._ipaddr_info` to carry the full address information into `_ipaddr_info` and avoid discarding IPv6 specific information.

Changelog entry & regression tests to come.

https://bugs.python.org/issue35545
(cherry picked from commit ac8eb8f)

Co-authored-by: Erwan Le Pape <lepaperwan@users.noreply.github.com>
miss-islington added a commit that referenced this pull request May 17, 2019
This PR proposes a solution to [bpo-35545](https://bugs.python.org/issue35545) by adding an optional `flowinfo` and `scopeid` to `asyncio.base_events._ipaddr_info` to carry the full address information into `_ipaddr_info` and avoid discarding IPv6 specific information.

Changelog entry & regression tests to come.

https://bugs.python.org/issue35545
(cherry picked from commit ac8eb8f)

Co-authored-by: Erwan Le Pape <lepaperwan@users.noreply.github.com>
@lepaperwan lepaperwan deleted the bpo-35545 branch March 29, 2022 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants