Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ussl: Fix polling of SSL sockets #9871

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

Merged
merged 1 commit into from
Dec 16, 2022
Merged

Conversation

damz
Copy link
Contributor

@damz damz commented Nov 7, 2022

What does this PR solve?

There are three issues in the current implementation ioctl(MP_STREAM_POLL) for SSL sockets:

  • mbedtls might have buffered data (which we can check with mbedtls_ssl_check_pending) which needs to be considered when checking for MP_STREAM_POLL_RD;
  • During handshake (and renegotiation) the state machine might need to write in order to read (or read in order to write);
  • An error raised during read() or write() needs to be stored so that we don't block on polling (e.g. we should not wait to read data on an SSL socket that is in error).

How does this PR solve the problem?

  • Store the direction the library told us it needs, and the last error when raised;
  • Only consider the direction the library to us it needed during ioctl(MP_STREAM_POLL);
  • Implement a full unit test in unix/coverage

@damz damz changed the title extmod/modussl_mbedtls: Consider mbedtls buffer when polling ussl: Consider mbedtls buffer when polling Nov 7, 2022
@damz damz changed the title ussl: Consider mbedtls buffer when polling ussl: Consider internal buffer when polling Nov 7, 2022
@dpgeorge dpgeorge added the extmod Relates to extmod/ directory in source label Nov 8, 2022
@dpgeorge
Copy link
Member

dpgeorge commented Nov 8, 2022

Thanks for the contribution. This is a good, simple fix, and since #5840 has not seen any progress (due to lack of time on my side) I suggest we merge this. Then #5840 can build on it.

@damz
Copy link
Contributor Author

damz commented Nov 8, 2022

@dpgeorge Thank you for pointing me in the right direction. After this I can send a separate PR implementing the write-for-read / read-for-write polling logic.

After this, the reminder of #5840 is the error logic change, but unfortunately I cannot say I understand why it is necessary.

@dpgeorge
Copy link
Member

dpgeorge commented Nov 8, 2022

After this I can send a separate PR implementing the write-for-read / read-for-write polling logic.

If it will only copy the work done in #5840, then it would be better to merge #5840 itself. The reason I didn't merge that PR yet is because I don't fully understand that logic. Maybe you can implement it in a simpler way?

the error logic change, but unfortunately I cannot say I understand why it is necessary

Let's leave that out for now.

@damz
Copy link
Contributor Author

damz commented Nov 8, 2022

I think you really just need this: damz@1166b86

The library tells you if it needs to read next or to write next. That is the only direction you are interested in for the next poll operation. Which operation you reenter after that doesn't matter, as both read and write paths reenter the handshake/renegotiation process.

@dpgeorge
Copy link
Member

dpgeorge commented Nov 8, 2022

I think you really just need this

That is definitely simpler than #5840. Did you have any test cases that show that your patch does indeed work?

@damz damz force-pushed the pr/mbedtls branch 2 times, most recently from 2dc0921 to b4b9c8b Compare November 10, 2022 06:16
@damz damz changed the title ussl: Consider internal buffer when polling ussl: Improve polling Nov 10, 2022
@codecov-commenter
Copy link

Codecov Report

Merging #9871 (2dc0921) into master (451ded8) will decrease coverage by 0.06%.
The diff coverage is 17.64%.

❗ Current head 2dc0921 differs from pull request most recent head b4b9c8b. Consider uploading reports for the commit b4b9c8b to get more accurate results

@@            Coverage Diff             @@
##           master    #9871      +/-   ##
==========================================
- Coverage   98.33%   98.27%   -0.07%     
==========================================
  Files         154      153       -1     
  Lines       20511    20526      +15     
==========================================
+ Hits        20170    20171       +1     
- Misses        341      355      +14     
Impacted Files Coverage Δ
extmod/modussl_mbedtls.c 79.67% <17.64%> (-6.40%) ⬇️
py/stream.h

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

damz added a commit to damz/micropython that referenced this pull request Nov 23, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
damz added a commit to damz/micropython that referenced this pull request Nov 23, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
damz added a commit to damz/micropython that referenced this pull request Nov 25, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
damz added a commit to damz/micropython that referenced this pull request Nov 28, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
damz added a commit to damz/micropython that referenced this pull request Dec 8, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
damz added a commit to damz/micropython that referenced this pull request Dec 15, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

damz added a commit to damz/micropython that referenced this pull request Dec 15, 2022
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
@damz
Copy link
Contributor Author

damz commented Dec 15, 2022

@dpgeorge I updated the code as you requested, and based on further experience with running this code over the last few weeks at Ribbit Network.

There is now a full unit test that runs in unix/coverage.

@damz damz changed the title ussl: Improve polling ussl: Fix polling of SSL sockets Dec 15, 2022
@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

@damz
Copy link
Contributor Author

damz commented Dec 15, 2022

As recommended by @Carglglz I reused the certificate/key pair already used in ssl_data.py.

During the initial handshake or subsequent renegotiation, the protocol
might need to read in order to write (or conversely to write in order
to read). It might be blocked from doing so by the state of the
underlying socket (i.e. there is no data to read, or there is no space
to write).

The library indicates this condition by returning one of the errors
`MBEDTLS_ERR_SSL_WANT_READ` or `MBEDTLS_ERR_SSL_WANT_WRITE`. When that
happens, we need to enforce that the next poll operation only considers
the direction that the library indicated.

In addition, mbedtls does its own read buffering that we need to take
into account while polling, and we need to save the last error between
read()/write() and ioctl().
@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 

@dpgeorge dpgeorge merged commit ed58d6e into micropython:master Dec 16, 2022
@dpgeorge
Copy link
Member

Thank you very much for the work on this, it's a nice clean change with a good test.

damz added a commit to damz/micropython that referenced this pull request Jan 19, 2023
A new boolean argument `dtls` is added to `ussl.wrap_socket()`,
if true the library assumes that the underlying socket is a
datagram socket (i.e. UDP or similar).

Implement our own timer callbacks as the out of the box
implementation relies on `gettimeofday()`.

To fully support asyncio for DTLS socket, we will need to return
a readable or writable event in `poll(MP_STREAM_POLL, ...)` if
`_mbedtls_timing_get_delay(self) >= 1`. This is left for future
work so as not to interfere with micropython#9871.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extmod Relates to extmod/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants