-
Notifications
You must be signed in to change notification settings - Fork 865
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
[BUG] Fixed: Detect reusable bindings and binding conflicts correctly #1588
Conversation
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.
CI build fails.
According to conversation #1513
Should be changed to:
Actually I collected what is discussed in #1513 (comment) as it contradicts with #1588 (comment) |
No, this isn't possible. If the number of users of a multiplexer drops to 0 (after every socket that uses it gets closed), the multiplexer is deleted. That's not a case at all. We start with a situation that we want to do
And that's exactly how sharing DOESN'T work :D If a multiplexer that matches the port number is found, it's being checked if it is in the way at all, and if so, if it is reusable, or conflicting. If you have a multiplexer with a specified IP address and you are trying to bind a different IP address, this multiplexer isn't in the way. In which case you create a new multiplexer. The multiplexer can be in the way only if:
If the multiplexer is in the way, it can then only be reusable or conflicting, so after making sure of that we need to check if this multiplexer is reusable, which is if:
Otherwise it's conflicting. There's also a small confusing situation with IPv6. If an existing binding is for :: address with IP6ONLY=false, then an attempt to bind IPv4 0.0.0.0 address is in conflict with it. Conversely, an attempted bind to IPv6 address :: with IP6ONLY=true is not conflict with (and also isn't in the way of) an existing binding to IPv4 0.0.0.0
But it doesn't match the current (and imaginable) state. First of all, there's no problem with reusing multiplexers of existing sockets, regardless of their state (unless they are broken and pending deletion, but that's another story). The only thing that is required is that it is allowed to be reused, and all UDP settings and IP address specification are the same. When you try to bind, there's a search over existing multiplexers (note that this means existing sockets using them). First we look for candidates - which is a multiplexer with the same port, and:
This is a selection of "potentially in the way". Then matching is if:
In all other cases this is a conflicted binding and is rejected. |
Once you call srt_close, socket is market for removal but not removed immediately. It's actually removed from garbage collector after some time. The same time multiplexer removed if there is no socket associated with given multiplexer. I tried to cover the case when given listener socket is closed, but not yet garbage collected, and new socket with the same settings is creating. This is a possible case. I agree that it may be hard to support but it's possible and easy to reproduce for sure as GC works with 1 second delay.
You probably missed that this condition has following pre-condition in my reply let me copy this precondition here again:
if I combine explicitly both conditions we will get the following: In your terminology this case is when existing socket is "in the way" and conflicting. so we cannot reuse it regardless of REUSEADDR value and reading your explanation above I see that I'm right and we're talking about the same things.
exactly.
exactly and this is exactly what I'm talking about in my version of this task description
please read my explanations above. I admit it may be not very intuitive but it's correct. Let me remove "existing multiplexer without active socket " case from it. here is what we have:
I've read your explanation and it match exactly to these two rules. But here is what you stated in the task description initially:
here is how I read fourth statement: |
That changes nothing. If the socket isn't yet internally removed, it is still a client o a multiplexer.
Yes, that could be an issue. I have also once fixed a bug when you close the listener socket and want to create another one with the same port, as this freeing of the listener port was also done only in GC. I fixed that by letting the socket be cleaned up by GC, but the listener occupation of the multiplexer is freed immediately. In general there's no problem at all to bind a caller socket to the same bindspec as an existing listener socket. You only cannot bind a listener socket to a bindspec that is already occupied by a listener socket (that is, you can bind, but after binding you cannot set this socket listen). The problem could be if you want to set up a listener with bindspec that is in conflict with an existing bindspec that remains after closing a listener socket (e.g.: set up a listener socket bound to 127.0.0.1:5000, close it, set up a new socket bound to 0.0.0.0:5000 and try to listen). This will fail, and could only succeed if you wait about 1 second. Kicking off a multiplexer in conflict would be a considerable idea, but the problem is that not in every case you can forcefully close a multiplexer.
Yeah, ok, that's correct. These rules are kinda confusing. :)
I still think that "regardless of REUSEADDR" etc. is confusing. In order to be able to bind you have to satisfy ALL of the conditions of case 2 here (with no regard to ordering at all):
But then, I think you have contributed well to the description and I think I can make it clearer with this. |
Made it with great pleasure. I've checked current task description and it's brilliant clear and precise, thank you! |
Co-authored-by: Maxim Sharabayko <maxlovic@gmail.com>
Unit test is failing in Travis.
|
Fixes #1503
The problem: the original procedure for finding a binding was only checking if the attempted binding can be matched with some other with the same port number. If found, it was reused, if settings allowed, otherwise a new binding was created. This was completely wrong because it didn't take into account a possibility that an existing binding may conflict with attempted binding and make it impossible to set up, as well as it was happening to reuse the binding that didn't exactly match the requested specification. The binding conflict would still result in error in the system
::bind
call in case when the multiplexer was decided to not be reused, but that was also misleading.Binding to
0.0.0.0:5000
could be allowed to be reused, if the original binding was REUSEADDR and if the attempted binding is also0.0.0.0:5000
. However, such a binding makes the port 5000 busy for all IPv4 addresses, so attempt to bind to127.0.0.1:5000
is impossible to set up, as the port 5000 is busy, as well as you cannot pretend that the binding to0.0.0.0:5000
covers this binding requirement. This situation wasn't so far handled at all, so there wasn't introduced any error reporting that situation.Similarly, if you attempt to bind to 1.2.3.4:5000 and such a binding is already found, then you can only go two ways:
Likely this attempt to create a binding could still fail, when the
CChannel
object is created and the inside UDP socket will be then tried to be bound to this address, and the conflict between either two the same IP and port sets or a specified IP would conflict with a wildcard, but then this would be reported misleadingly through the systembind
function. The problem is that this error would be a consequence of rejection of binding reusage.Beside this "relying on system error", however, this procedure was also conaining an error by allowing that an existing binding to given port be reused as long as it has correct UDP-related settings and the same port, "winking in" an existing binding that didn't carry the same setup.
The new approach considers the following: the existing multiplexers are searched for an intersecting binding, that is, one that covers the same pool of addresses, at least partially.
If no intersecting binding was found, a new multiplexer is created for that socket.
If found, IP addresses must be identical (plus the IPV6ONLY flag) and additionally both must have REUSEADDR=true and identical UDP settings (as it was before). If an intersecting multiplexer was found, but not identical, it results in a conflict.