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
net: context: connect: Make sure local end is bound before connecting #785
Conversation
Proposed fix for #752 (comment) . If approved, call to net_context_bind_default() might be added to some other functions (in separate patches). |
subsys/net/ip/net_context.c
Outdated
return -EADDRNOTAVAIL; | ||
} | ||
|
||
if (dest_addr->family == AF_INET6) { |
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.
I think we should put
#if defined(CONFIG_NET_IPV{6|4})
...
#endif /* ... */
checks around the family checkings, so similar way as what is done in other functions.
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.
Yup, fixed.
subsys/net/ip/net_context.c
Outdated
static int net_context_bind_default(struct net_context *context, | ||
const struct sockaddr *dest_addr) | ||
{ | ||
struct net_if *iface = net_if_get_default(); |
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.
If the interface is already set for the context, then this should not be done. If the app / other part of the stack has already set the interface, this code now overwrites that value.
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.
I may imagine you're commented on the wrong line. Anyway, two points: 1) This code sets the local address, which is bound to an interface, so if it sets address, it also sets interface. 2) I spent reasonable time to look into code, and on "outgoing" path, only bind() sets local address and iface (all other paths appear to be for incoming packets/connections). 3) Final point obvious from code is that iface is not set if address is already set, because p.1 (they always set together).
subsys/net/ip/net_context.c
Outdated
(struct in6_addr *)net_ipv6_unspecified_address(); | ||
|
||
return 0; | ||
} else if (dest_addr->family == AF_INET) { |
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.
There should be no need for "else" here as if we enter the ipv6 branch, we always return from it. This way it is cleaner to put the pre-processor if's around this block.
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.
Yup
subsys/net/ip/net_context.c
Outdated
@@ -689,6 +689,45 @@ int net_context_put(struct net_context *context) | |||
return 0; | |||
} | |||
|
|||
static int net_context_bind_default(struct net_context *context, |
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.
As this is internal function, perhaps calling this bind_default() would be ok.
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.
Fixed
subsys/net/ip/net_context.c
Outdated
|
||
if (!iface) { | ||
NET_ERR("Cannot bind to %s", | ||
net_sprint_ipv6_addr(&addr6->sin6_addr)); |
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.
Isn't there compilation error here if debugging is enabled as I do not see add6 defined anywhere? And we bind to any address later in this function so this debug print would give wrong info 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.
Copy-paste error, will remove refs to ipv6.
subsys/net/ip/net_context.c
Outdated
NET_ERR("Cannot bind to %s", | ||
net_sprint_ipv6_addr(&addr6->sin6_addr)); | ||
|
||
return -EADDRNOTAVAIL; |
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.
The interface checking needs to be done differently, perhaps something like this
if (!net_context_get_iface(context)) {
net_context_set_iface(context, net_if_get_default());
if (!net_context_get_iface(context)) {
return -ENOENT;
}
}
although I am not sure if we really need to check if the default interface returned NULL, probably NET_ASSERT() would be sufficient here.
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.
Sorry, this code copied from net_context_bind(). If you think checking needs to be done differently, then net_context_bind() needs to do it, but I don't see anything wrong with what it does. Again, bind_default() function performs the same operation as bind(.., INADDR_ANY, ...), the reason I don't use bind() directly is because it requires munging with address families to use particular INADDR_ANY impl for each. (bind_default also checks before binds.)
net_sin6_ptr(&context->local)->sin6_family = AF_INET6; | ||
net_sin6_ptr(&context->local)->sin6_addr = | ||
(struct in6_addr *)net_ipv6_unspecified_address(); | ||
|
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.
We need to check the port number here too. The net_context_get() sets the initial value for the port, but it might be used already when we get into this point. So we need to check it by calling check_used_port() like the net_context_bind() is doing, and probably call find_available_port() again if the port was used already (unlikely to happen thou).
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.
Let's treat this as a separate issue. So far, I indeed rely that random port was set at the time of context creation. As you say, that's actually too early, and may be already used at the time of actual network operation. But the obvious solution is to move setting random port to bind_default(), which is a separate patch, requiring separate consideration and testing (UDP cases, etc.)
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.
Fair enough, lets deal with the port issue in later patches.
subsys/net/ip/net_context.c
Outdated
@@ -689,6 +689,45 @@ int net_context_put(struct net_context *context) | |||
return 0; | |||
} | |||
|
|||
static int net_context_bind_default(struct net_context *context, | |||
const struct sockaddr *dest_addr) |
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.
Having dest_addr here is great, but the function is using it only for family currently. So the code is missing a call to
src_addr = net_if_ipv6_select_src_addr(NULL, dest_addr);
and then the src_addr can be used to get the interface by calling
if (!net_if_ipv6_addr_lookup(src_addr, &iface)) {
return -EINVAL;
}
If the caller does not supply the dest_addr, then we can use the default interface. For IPv4 there is no source address selection function currently, so we can just use the default interface for IPv4.
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.
Actually realized that the dest_addr cannot be null as then we would not know the family.
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.
Yes, dest_addr is passed so we can select address family. My initial idea was just to a special case to existing net_context_bind(), when addr=NULL is passed to it, perform this default binding. But then we wouldn't know with AF to select indeed. So instead, I factored out this logic into separate function, which accepts dest address. It's called from connect(), where destaddr is known, and I may imagine it may need to be called form recv(), send() on UDP path, where destaddr is also known.
So, that's the reason why this function is written like it is - it mimics net_context_bind(). Note that's not the only way to select AF, because context is created with specific AF ;-). But doing it like bind() does kinda leave possibility open to have Linux-style dual-family sockets.
BTW, we can shorten the commit subject a bit, so "net: context: ..." is enough, no need to put the ": connect" there. |
Well, it fits within limit, and I'm proponent on commit subjects which give more info, not less (e.g., as I mentioned above, similar change may need to be applied to send()/recv()). |
7079640
to
1be5ec9
Compare
Ok, fixes applied and shippable passes. @jukkar : Please re-review. |
subsys/net/ip/net_context.c
Outdated
@@ -689,6 +689,49 @@ int net_context_put(struct net_context *context) | |||
return 0; | |||
} | |||
|
|||
static int bind_default(struct net_context *context, | |||
const struct sockaddr *dest_addr) |
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.
Indentation went wrong after the function name was shortened.
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.
Fixed
subsys/net/ip/net_context.c
Outdated
static int bind_default(struct net_context *context, | ||
const struct sockaddr *dest_addr) | ||
{ | ||
struct net_if *iface = net_if_get_default(); |
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.
If we have multiple interfaces, then this selection might get wrong interface.
If the user has supplied a real destination address (and not just unspecified address), then we would need to select the interface according to that dest address. Only if the user does not set the dest address, then we can use the default interface. I wrote a small piece of code for that in my earlier review.
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.
If we have multiple interfaces, then this selection might get wrong interface.
Yes, it might. Just the same as net_context_bind(). My assumption is that if explicit bind() is not called, it's called implicitly as bind(INADDR_ANY, rand()). bind_default() just performs the same operations as next_context_bind(INADDR_ANY, rand()), using the same code (the reason I factored out code into new function instead of just calling next_context_bind() because it seemed like a chore to construct INADDR_ANY for IPv4 vs IPv6 cases; maybe that was wrong actually).
I can paste your code in, but the only expected outcome from that would be that implementation (and thus behavior) of explicit vs implicit bind diverge, and thus lead to confusion. Note that neither next_context_bind() nor your proposed change to bind_default() is correct impl of INADDR_ANY - per POSIX, it should bind to all not to default/"best" interface.
net_sin6_ptr(&context->local)->sin6_family = AF_INET6; | ||
net_sin6_ptr(&context->local)->sin6_addr = | ||
(struct in6_addr *)net_ipv6_unspecified_address(); | ||
|
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.
Fair enough, lets deal with the port issue in later patches.
Introduce net_context_bind_default() to ensure that local address is set for context if not yet (via explict bind() call). This fixes dereferences of NULL pointer to local address which was exposed when MMU was enabled for qemu_x86. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
1be5ec9
to
b8bf80c
Compare
Closed in favor of #817 |
Introduce net_context_bind_default() to ensure that local address is
set for context if not yet (via explict bind() call). This fixes
dereferences of NULL pointer to local address which was exposed when
MMU was enabled for qemu_x86.
Signed-off-by: Paul Sokolovsky paul.sokolovsky@linaro.org