Skip to content

tls: use custom instead of socket based bio#12911

Merged
mattklein123 merged 7 commits intoenvoyproxy:masterfrom
florincoras:tls
Sep 4, 2020
Merged

tls: use custom instead of socket based bio#12911
mattklein123 merged 7 commits intoenvoyproxy:masterfrom
florincoras:tls

Conversation

@florincoras
Copy link
Copy Markdown
Member

@florincoras florincoras commented Sep 1, 2020

Signed-off-by: Florin Coras fcoras@cisco.com

Solves #12899

Risk Level: Medium
Testing: unit tests
Docs Changes: n/a
Release Notes: tls: switched from using socket BIOs to using custom BIOs that know how to interact with IoHandles. The feature can be disabled by setting runtime feature envoy.reloadable_features.tls_use_io_handle_bio to false.

Signed-off-by: Florin Coras <fcoras@cisco.com>
@florincoras
Copy link
Copy Markdown
Member Author

cc @antoniovicente @mattklein123

@antoniovicente
Copy link
Copy Markdown
Contributor

Please mention issue #12899 in the PR description.

I need to look at it more closely but it seems more or less right. It may be worth adding a separate header for BIO_new_io_handle

Copy link
Copy Markdown
Member

@lizan lizan left a comment

Choose a reason for hiding this comment

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


namespace {

static inline Envoy::Network::IoHandle* bio_io_handle(BIO* bio) {
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.

static doesn't do anything in unnamed namespace :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for that! Wanted to maintain the "typical" BIO formatting but even clang_tidy is complaining now. Is there any macro/comment that we could use to disable clang_tidy for this file? Do we want that or should I rename everything?

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.

I'm fine with keeping the naming, but not static. You can add NOLINT or NOLINTNEXTLINE for each method.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks and done!

- added io_handle_bio header file
- removed static keyword for functions in unnamed namespace
- added simple unit tests for io_handle_bio member functions

Signed-off-by: Florin Coras <fcoras@cisco.com>
name = "ssl_socket_lib",
srcs = ["ssl_socket.cc"],
hdrs = ["ssl_socket.h"],
srcs = [
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.

Can io_handle_bio.{cc,h} in a separate envoy_cc_library?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done!

Signed-off-by: Florin Coras <fcoras@cisco.com>
Copy link
Copy Markdown
Contributor

@antoniovicente antoniovicente left a comment

Choose a reason for hiding this comment

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

Thanks for the cleanup. One less need for direct access to the fd.

And sorry for the delay in review, it has been a busy week.

// socket BIO which relies on access to a file descriptor.
BIO* bio = BIO_new_socket(callbacks_->ioHandle().fdDoNotUse(), 0);
// Use custom BIO that reads from/writes to IoHandle
BIO* bio = BIO_new_io_handle(&callbacks_->ioHandle());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider protecting with a runtime feature to allow for fast rollback if we find some problem despite it being relatively simple and as far I can tell, correct.

Copy link
Copy Markdown
Member Author

@florincoras florincoras Sep 3, 2020

Choose a reason for hiding this comment

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

Okay, let me add that. But, since I'm not familiar with them, will switching be possible at runtime or does the code have to be recompiled with the feature moved from enabled to disabled list?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My understanding is that it is controllable via reloadable config. The default is to use the new code path. Since this call only happens once per connection, during initialization of the transport socket object, it is safe for the runtime feature to change during runtime.

case BIO_C_GET_FD:
ret = -1;
*reinterpret_cast<void**>(ptr) = b->ptr;
break;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we really need to implement BIO_C_SET_FD and BIO_C_GET_FD?

It may be good to add ASSERT(false) for cases that we do not expect to ever happen at the proxy. This includes: BIO_CTRL_RESET, BIO_C_FILE_SEEK, BIO_C_FILE_TELL, BIO_CTRL_INFO, BIO_C_GET_FD, BIO_C_SET_FD:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've used the socket bio as an example (since we're faking one) and those are the only control messages it implements. BIO_C_SET_FD is the one I've used for setting the ptr but I guess we could change that.
What would you prefer, keep following the socket model or implement things differently?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your constructor could set b->ptr directly instead of using BIO_C_SET_FD.

I think your implementation of iohandle BIOs is a great improvement, I'm just working my way through some nits.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ow. Yes we can, but we need to initialize all the other fields (shutdown, init, num). Should I do that explicitly in the constructor and then ASSERT(false) on BIO_C_SET_FD and BIO_C_GET_FD?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, we'ld need to also initiate other relevant fields like init also in the contructor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Perfect! Done!

const BIO_METHOD* BIO_s_io_handle(void);

// NOLINTNEXTLINE(readability-identifier-naming)
BIO* BIO_new_io_handle(Envoy::Network::IoHandle* io_handle);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it be possible to add some comments for this factory method?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done!

namespace Tls {

// NOLINTNEXTLINE(readability-identifier-naming)
const BIO_METHOD* BIO_s_io_handle(void);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This method seems to only be used in the cc file, should we remove it from the header?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point. I exposed it for testing but ended up not using it.

auto result = bio_io_handle(b)->writev(&slice, 1);
BIO_clear_retry_flags(b);
if (!result.ok()) {
if (result.err_->getErrorCode() == Api::IoError::IoErrorCode::Again) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We may also want to set retry in the case of Api::IoError::IoErrorCode::Interrupt.

Although it seems that writes returning Interrupt will result in Envoy closing the socket. I don't know how often a EINTR return would happen in practice.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Interesting. I guess we could treat EINTR as EAGAIN here.

ret = bio_->method->ctrl(bio_, BIO_CTRL_GET_CLOSE, 0, nullptr);
EXPECT_EQ(ret, 1);

// avoid BIO_free assert in destructor
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The bio_->init = 1 and bio_->shutdown = 1 further down seem to undo the work done by this BIO_CTRL_SET_CLOSE call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Removed this test. It was crashing only prior to adding the last one.

b->shutdown = int(num);
break;
case BIO_CTRL_FLUSH:
ret = 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

test for this case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I didn't add a test for this because it's exercised by the rest of the boringssl infra in other tests, but let me add a simple one for completion.

antoniovicente
antoniovicente previously approved these changes Sep 3, 2020
Copy link
Copy Markdown
Contributor

@antoniovicente antoniovicente left a comment

Choose a reason for hiding this comment

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

Looks great, thanks for the improvements to the socket APIs.

Signed-off-by: Florin Coras <fcoras@cisco.com>
@florincoras
Copy link
Copy Markdown
Member Author

Looks great, thanks for the improvements to the socket APIs.

Thank you for the review and help with this!

N.B. Pushed a fix for clang-tidy's complaints regarding the use of nullptr

antoniovicente
antoniovicente previously approved these changes Sep 3, 2020
Copy link
Copy Markdown
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Awesome, thanks, one quick high level comment.

/wait

"envoy.reloadable_features.preserve_upstream_date",
"envoy.reloadable_features.stop_faking_paths",
"envoy.reloadable_features.strict_1xx_and_204_response_headers",
"envoy.reloadable_features.tls_use_io_handle_bio",
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 should be documented somewhere. Can you add a release note for this and also update the PR description to mention it? This will help us track it for removal later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done! Let me know if I missed something because this is my first time doing release notes updates.

@mattklein123 mattklein123 self-assigned this Sep 4, 2020
Signed-off-by: Florin Coras <fcoras@cisco.com>
Signed-off-by: Florin Coras <fcoras@cisco.com>
Signed-off-by: Florin Coras <fcoras@cisco.com>
Copy link
Copy Markdown
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Nice!

@mattklein123 mattklein123 merged commit 1663949 into envoyproxy:master Sep 4, 2020
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.

4 participants