-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
syscall: refactor address APIs for deeper errno latching #3897
Changes from 1 commit
4b581b3
6c65b13
36c81e5
bb2841e
7e05d55
dc0c613
42d0771
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ | |
#include <cstdint> | ||
#include <memory> | ||
#include <string> | ||
#include <tuple> | ||
|
||
#include "envoy/common/pure.h" | ||
|
||
|
@@ -17,6 +16,22 @@ namespace Envoy { | |
namespace Network { | ||
namespace Address { | ||
|
||
/** | ||
* Result holds the rc and errno values resulting from a system call. | ||
*/ | ||
struct Result { | ||
|
||
/** | ||
* The return code from the system call. | ||
*/ | ||
int rc; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Struct/Class member variables have a _ postfix (e.g., int foo_;). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in bb2841e. |
||
|
||
/** | ||
* The errno value as captured after the system call. | ||
*/ | ||
int error; | ||
}; | ||
|
||
/** | ||
* Interface for an Ipv4 address. | ||
*/ | ||
|
@@ -129,19 +144,19 @@ class Instance { | |
* Bind a socket to this address. The socket should have been created with a call to socket() on | ||
* an Instance of the same address family. | ||
* @param fd supplies the platform socket handle. | ||
* @return a tuple of <0, errno> for success and <-1, errno> for failure. If the call is | ||
* successful, errno shouldn't be used. | ||
* @return a Result with rc = 0 for success and rc = -1 for failure. If the call is successful, | ||
* the error value shouldn't be used. | ||
*/ | ||
virtual std::tuple<int, int> bind(int fd) const PURE; | ||
virtual Result bind(int fd) const PURE; | ||
|
||
/** | ||
* Connect a socket to this address. The socket should have been created with a call to socket() | ||
* on this object. | ||
* @param fd supplies the platform socket handle. | ||
* @return a tuple of <0, errno> for success and <-1, errno> for failure. If the call is | ||
* successful, errno shouldn't be used. | ||
* @return a Result with rc = 0 for success and rc = -1 for failure. If the call is successful, | ||
* the error value shouldn't be used. | ||
*/ | ||
virtual std::tuple<int, int> connect(int fd) const PURE; | ||
virtual Result connect(int fd) const PURE; | ||
|
||
/** | ||
* @return the IP address information IFF type() == Type::Ip, otherwise nullptr. | ||
|
@@ -151,11 +166,11 @@ class Instance { | |
/** | ||
* Create a socket for this address. | ||
* @param type supplies the socket type to create. | ||
* @return a tuple of <fd, errno> for success and <-1, errno> for failure, where fd represents | ||
* the file descriptor naming the socket. If a valid file descriptor is returned, errno | ||
* @return a Result with rc = fd for success and rc = -1 for failure, where fd represents the | ||
* file descriptor naming the socket. If a valid file descriptor is returned, the error value | ||
* shouldn't be used. | ||
*/ | ||
virtual std::tuple<int, int> socket(SocketType type) const PURE; | ||
virtual Result socket(SocketType type) const PURE; | ||
|
||
/** | ||
* @return the type of address. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,7 +133,7 @@ InstanceConstSharedPtr peerAddressFromFd(int fd) { | |
return addressFromSockAddr(ss, ss_len); | ||
} | ||
|
||
std::tuple<int, int> InstanceBase::socketFromSocketType(SocketType socketType) const { | ||
Result InstanceBase::socketFromSocketType(SocketType socketType) const { | ||
#if defined(__APPLE__) | ||
int flags = 0; | ||
#else | ||
|
@@ -168,7 +168,7 @@ std::tuple<int, int> InstanceBase::socketFromSocketType(SocketType socketType) c | |
RELEASE_ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) != -1, ""); | ||
#endif | ||
|
||
return std::make_tuple(fd, errno); | ||
return {fd, errno}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the On the other hand, there's already a RELEASE_ASSERT that an error did not occur (fd != -1). So should this function even return errno? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ggreenway good catch! What do you suggest? Should we still keep the function return type as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say leave this function as returning an int, and make sure the docs mention that this will never fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in dc0c613. |
||
} | ||
|
||
Ipv4Instance::Ipv4Instance(const sockaddr_in* address) : InstanceBase(Type::Ip) { | ||
|
@@ -212,21 +212,19 @@ bool Ipv4Instance::operator==(const Instance& rhs) const { | |
(ip_.port() == rhs_casted->ip_.port())); | ||
} | ||
|
||
std::tuple<int, int> Ipv4Instance::bind(int fd) const { | ||
return std::make_tuple(::bind(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv4_.address_), | ||
sizeof(ip_.ipv4_.address_)), | ||
errno); | ||
Result Ipv4Instance::bind(int fd) const { | ||
return {::bind(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv4_.address_), | ||
sizeof(ip_.ipv4_.address_)), | ||
errno}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's quite possible that C++ guarantees that the ::bind call happens first, and then errno is captured, but TBH it's not very obvious to me that is the case. I would prefer that we do the call into a local variable and then return the result and errno. Same elsewhere. Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. The c++14 documentation states:
I'll fix this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @venilnoronha You're referring to function arguments, but this is list-initialization, and C++14 guarantees the order:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lizan my bad, I just realized that we switched over to a I think I'd still have it explicit, at least for readability. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 7e05d55. |
||
} | ||
|
||
std::tuple<int, int> Ipv4Instance::connect(int fd) const { | ||
return std::make_tuple(::connect(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv4_.address_), | ||
sizeof(ip_.ipv4_.address_)), | ||
errno); | ||
Result Ipv4Instance::connect(int fd) const { | ||
return {::connect(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv4_.address_), | ||
sizeof(ip_.ipv4_.address_)), | ||
errno}; | ||
} | ||
|
||
std::tuple<int, int> Ipv4Instance::socket(SocketType type) const { | ||
return socketFromSocketType(type); | ||
} | ||
Result Ipv4Instance::socket(SocketType type) const { return socketFromSocketType(type); } | ||
|
||
absl::uint128 Ipv6Instance::Ipv6Helper::address() const { | ||
absl::uint128 result{0}; | ||
|
@@ -279,25 +277,24 @@ bool Ipv6Instance::operator==(const Instance& rhs) const { | |
(ip_.port() == rhs_casted->ip_.port())); | ||
} | ||
|
||
std::tuple<int, int> Ipv6Instance::bind(int fd) const { | ||
return std::make_tuple(::bind(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv6_.address_), | ||
sizeof(ip_.ipv6_.address_)), | ||
errno); | ||
Result Ipv6Instance::bind(int fd) const { | ||
return {::bind(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv6_.address_), | ||
sizeof(ip_.ipv6_.address_)), | ||
errno}; | ||
} | ||
|
||
std::tuple<int, int> Ipv6Instance::connect(int fd) const { | ||
return std::make_tuple(::connect(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv6_.address_), | ||
sizeof(ip_.ipv6_.address_)), | ||
errno); | ||
Result Ipv6Instance::connect(int fd) const { | ||
return {::connect(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv6_.address_), | ||
sizeof(ip_.ipv6_.address_)), | ||
errno}; | ||
} | ||
|
||
std::tuple<int, int> Ipv6Instance::socket(SocketType type) const { | ||
const std::tuple<int, int> result = socketFromSocketType(type); | ||
const int fd = std::get<0>(result); | ||
|
||
Result Ipv6Instance::socket(SocketType type) const { | ||
const Result result = socketFromSocketType(type); | ||
// Setting IPV6_V6ONLY resticts the IPv6 socket to IPv6 connections only. | ||
const int v6only = ip_.v6only_; | ||
RELEASE_ASSERT(::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != -1, ""); | ||
RELEASE_ASSERT(::setsockopt(result.rc, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != -1, | ||
""); | ||
return result; | ||
} | ||
|
||
|
@@ -340,33 +337,29 @@ PipeInstance::PipeInstance(const std::string& pipe_path) : InstanceBase(Type::Pi | |
|
||
bool PipeInstance::operator==(const Instance& rhs) const { return asString() == rhs.asString(); } | ||
|
||
std::tuple<int, int> PipeInstance::bind(int fd) const { | ||
Result PipeInstance::bind(int fd) const { | ||
if (abstract_namespace_) { | ||
return std::make_tuple(::bind(fd, reinterpret_cast<const sockaddr*>(&address_), | ||
offsetof(struct sockaddr_un, sun_path) + address_length_), | ||
errno); | ||
return {::bind(fd, reinterpret_cast<const sockaddr*>(&address_), | ||
offsetof(struct sockaddr_un, sun_path) + address_length_), | ||
errno}; | ||
} | ||
// Try to unlink an existing filesystem object at the requested path. Ignore | ||
// errors -- it's fine if the path doesn't exist, and if it exists but can't | ||
// be unlinked then `::bind()` will generate a reasonable errno. | ||
unlink(address_.sun_path); | ||
return std::make_tuple(::bind(fd, reinterpret_cast<const sockaddr*>(&address_), sizeof(address_)), | ||
errno); | ||
return {::bind(fd, reinterpret_cast<const sockaddr*>(&address_), sizeof(address_)), errno}; | ||
} | ||
|
||
std::tuple<int, int> PipeInstance::connect(int fd) const { | ||
Result PipeInstance::connect(int fd) const { | ||
if (abstract_namespace_) { | ||
return std::make_tuple(::connect(fd, reinterpret_cast<const sockaddr*>(&address_), | ||
offsetof(struct sockaddr_un, sun_path) + address_length_), | ||
errno); | ||
return {::connect(fd, reinterpret_cast<const sockaddr*>(&address_), | ||
offsetof(struct sockaddr_un, sun_path) + address_length_), | ||
errno}; | ||
} | ||
return std::make_tuple( | ||
::connect(fd, reinterpret_cast<const sockaddr*>(&address_), sizeof(address_)), errno); | ||
return {::connect(fd, reinterpret_cast<const sockaddr*>(&address_), sizeof(address_)), errno}; | ||
} | ||
|
||
std::tuple<int, int> PipeInstance::socket(SocketType type) const { | ||
return socketFromSocketType(type); | ||
} | ||
Result PipeInstance::socket(SocketType type) const { return socketFromSocketType(type); } | ||
|
||
} // namespace Address | ||
} // namespace Network | ||
|
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.
Q: Is this really address specific? Should we potentially put this in https://github.com/envoyproxy/envoy/blob/master/include/envoy/api/os_sys_calls.h as SyscalResult or something like that? I don't feel that strongly but throwing it out there.
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.
That makes perfect sense. I wanted to use this
struct
in other places (buffer
, etc.) anyway.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.
Addressed in bb2841e.