Skip to content

Conversation

@jimklimov
Copy link
Member

@jimklimov jimklimov commented Aug 5, 2023

...and fix some related issues

Closes: #2012

Testing:

  • allowing "IPv4-mapped IPv6" mode, expected to be default for most OSes now, getting one socket for both IP versions:
...
Aug 05 22:56:41 pve nut-server[1767496]:    0.000240        [D3] setuptcp: try to bind to * port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000245        [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
Aug 05 22:56:41 pve nut-server[1767496]:    0.000250        [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
Aug 05 22:56:41 pve nut-server[1767496]:    0.000254        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000273        listening on 0.0.0.0 port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000278        [D3] setuptcp: Could bind to 0.0.0.0:3493 trying to handle a 'LISTEN *' directive; will release it for now to try IPv6
Aug 05 22:56:41 pve nut-server[1767496]:    0.000287        [D3] setuptcp: try to bind to ::0 port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000299        listening on ::0 port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000303        [D3] setuptcp: try taking IPv4 'ANY' again (if dual-stack IPv6 'ANY' did not grab it)
Aug 05 22:56:41 pve nut-server[1767496]:    0.000307        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000316        [D3] setuptcp: bind: Address already in use
Aug 05 22:56:41 pve nut-server[1767496]:    0.000322        not listening on 0.0.0.0 port 3493
Aug 05 22:56:41 pve nut-server[1767496]:    0.000327        [D3] setuptcp: Could not bind to IPv4 0.0.0.0:3493 after trying to bind to IPv6: assuming dual-stack support on this system
Aug 05 22:56:41 pve nut-server[1767496]:    0.000331        [D3] setuptcp: remembering IPv6 'ANY' instead of 'LISTEN *'
...

:; netstat -anp | grep 3493
tcp        0      0 127.0.0.1:36442         127.0.0.1:3493          ESTABLISHED 1767499/upsmon
tcp6       0      0 :::3493                 :::*                    LISTEN      1767496/upsd
tcp6       0      0 127.0.0.1:3493          127.0.0.1:36442         ESTABLISHED 1767496/upsd
  • custom build with required "IPv6-only" mode, getting two sockets:
...
Aug 05 22:51:49 pve nut-server[1751573]:    0.000160        [D3] setuptcp: try to bind to * port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000164        [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
Aug 05 22:51:49 pve nut-server[1751573]:    0.000167        [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
Aug 05 22:51:49 pve nut-server[1751573]:    0.000169        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000181        listening on 0.0.0.0 port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000184        [D3] setuptcp: Could bind to 0.0.0.0:3493 trying to handle a 'LISTEN *' directive; will release it for now to try IPv6
Aug 05 22:51:49 pve nut-server[1751573]:    0.000190        [D3] setuptcp: try to bind to ::0 port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000197        listening on ::0 port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000200        [D3] setuptcp: try taking IPv4 'ANY' again (if dual-stack IPv6 'ANY' did not grab it)
Aug 05 22:51:49 pve nut-server[1751573]:    0.000202        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000208        listening on 0.0.0.0 port 3493
Aug 05 22:51:49 pve nut-server[1751573]:    0.000211        [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'
Aug 05 22:51:49 pve nut-server[1751573]:    0.000213        [D3] setuptcp: also remembering IPv6 'ANY' instead of 'LISTEN *'
Aug 05 22:51:49 pve nut-server[1751573]:    0.000218        [D6] setuptcp: SKIP bind to ::0 port 3493: entry already initialized
...

:; netstat -anp | grep 3493
tcp        0      0 0.0.0.0:3493            0.0.0.0:*               LISTEN      1751573/upsd
tcp        0      0 127.0.0.1:3493          127.0.0.1:50286         ESTABLISHED 1751573/upsd
tcp        0      0 127.0.0.1:50286         127.0.0.1:3493          ESTABLISHED 1749655/upsmon
tcp6       0      0 :::3493                 :::*                    LISTEN      1751573/upsd

CC @gdt @svarshavchik @clepple @aquette

… first [networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…v6) due to lack of LISTEN directive [networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
… and end the loop while there seem to be more resolved addresses for it [networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…Pv6 addresses, try to disable IPv4-mapping support [networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@jimklimov jimklimov added enhancement documentation service/daemon start/stop General subject for starting and stopping NUT daemons (drivers, server, monitor); also BG/FG/Debug labels Aug 5, 2023
@jimklimov jimklimov added this to the 2.8.1 milestone Aug 5, 2023
@jimklimov jimklimov requested review from aquette and clepple August 5, 2023 21:00
@jimklimov
Copy link
Member Author

Nice, nobody complained!

…ically (for both IPv4 and IPv6 if we can) [networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@jimklimov
Copy link
Member Author

jimklimov commented Aug 6, 2023

Also checked in a Windows build (MinGW x64) which out of the box is not dual-stack here:

$ NUT_CONFPATH=`pwd`/tests/NIT/tmp/etc ./server/upsd
Network UPS Tools upsd 2.8.0-2279-g6cdb3fcf8
   0.000000     [D1] debug level is '6'
   0.003030     [D3] setuptcp: try to bind to * port 3493
   0.005569     [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
   0.009464     [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
   0.013465     [D3] setuptcp: try to bind to 0.0.0.0 port 3493
   0.018793     listening on 0.0.0.0 port 3493
   0.021141     [D3] setuptcp: Could bind to 0.0.0.0:3493 trying to handle a 'LISTEN *' directive; will release it for now to try IPv6
   0.027475     [D3] setuptcp: try to bind to ::0 port 3493
   0.031496     listening on ::0 port 3493
   0.033224     [D3] setuptcp: try taking IPv4 'ANY' again (if dual-stack IPv6 'ANY' did not grab it)
   0.038031     [D3] setuptcp: try to bind to 0.0.0.0 port 3493
   0.041117     listening on 0.0.0.0 port 3493
   0.043530     [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'
   0.047428     [D3] setuptcp: also remembering IPv6 'ANY' instead of 'LISTEN *'
   0.051026     [D6] setuptcp: SKIP bind to ::0 port 3493: entry already initialized
   0.054955     [D1] Can not become_user(<null>): not implemented on this platform
   0.059194     [D2] sstate_connect: preparing Windows pipe dummy-ups-UPS2
   0.063019     [D2] sstate_connect: failed to WaitNamedPipe(\\.\pipe\dummy-ups-UPS2)
...

As a platform note, it seems to have had no qualms about listening several times on the same host:port though, at least for ANY addresses (here :: comes into picture twice, explicitly and via *):

$ cat tests/NIT/tmp/etc/upsd.conf
STATEPATH "/home/jim/nut-win/tests/NIT/tmp/run"
#LISTEN localhost 34988
LISTEN * 34988
LISTEN 127.0.0.1 34988
LISTEN ::1 34988
LISTEN :: 34988
DEBUG_MIN 6
ALLOW_NO_DEVICE true


$ NUT_CONFPATH=`pwd`/tests/NIT/tmp/etc ./server/upsd
Network UPS Tools upsd 2.8.0-2279-g6cdb3fcf8
   0.002635     [D3] setuptcp: try to bind to * port 34988
   0.005134     [D1] setuptcp: handling 'LISTEN * 34988' with IPv4 any-address support
   0.008602     [D1] setuptcp: handling 'LISTEN * 34988' with IPv6 any-address support
   0.012353     [D3] setuptcp: try to bind to 0.0.0.0 port 34988
   0.017838     listening on 0.0.0.0 port 34988
   0.019943     [D3] setuptcp: Could bind to 0.0.0.0:34988 trying to handle a 'LISTEN *' directive; will release it for now to try IPv6
   0.026046     [D3] setuptcp: try to bind to ::0 port 34988
   0.029085     listening on ::0 port 34988
   0.030892     [D3] setuptcp: try taking IPv4 'ANY' again (if dual-stack IPv6 'ANY' did not grab it)
   0.035253     [D3] setuptcp: try to bind to 0.0.0.0 port 34988
   0.038188     listening on 0.0.0.0 port 34988
   0.040555     [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'
   0.043648     [D3] setuptcp: also remembering IPv6 'ANY' instead of 'LISTEN *'
   0.047282     [D6] setuptcp: SKIP bind to ::0 port 34988: entry already initialized
   0.050674     [D3] setuptcp: try to bind to 127.0.0.1 port 34988
   0.053794     listening on 127.0.0.1 port 34988
   0.056130     [D3] setuptcp: try to bind to ::1 port 34988
   0.059295     listening on ::1 port 34988
   0.061435     [D3] setuptcp: try to bind to :: port 34988
   0.064261     listening on :: port 34988
...

$ netstat -an | grep 34988
  TCP    0.0.0.0:34988          0.0.0.0:0              LISTENING
  TCP    127.0.0.1:34988        0.0.0.0:0              LISTENING
  TCP    [::]:34988             [::]:0                 LISTENING
  TCP    [::]:34988             [::]:0                 LISTENING
  TCP    [::1]:34988            [::]:0                 LISTENING

...and can achieve even more by duplicating upsd.conf lines:

$ netstat -an | grep 34988
  TCP    0.0.0.0:34988          0.0.0.0:0              LISTENING
  TCP    127.0.0.1:34988        0.0.0.0:0              LISTENING
  TCP    127.0.0.1:34988        0.0.0.0:0              LISTENING
  TCP    [::]:34988             [::]:0                 LISTENING
  TCP    [::]:34988             [::]:0                 LISTENING
  TCP    [::]:34988             [::]:0                 LISTENING
  TCP    [::1]:34988            [::]:0                 LISTENING
  TCP    [::1]:34988            [::]:0                 LISTENING

…ality of `require_IPV6_V6ONLY` for one use-case [networkupstools#2013 review, networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@jimklimov
Copy link
Member Author

jimklimov commented Aug 14, 2023

Revised upsd.c. Mostly went for the previous chain of events for this iteration, just dropping the optional ipv6only handling - requesting it always now (whether it works on OS side is another matter).

So it asks for IPv4 "any addr" first to check it is available on that port, then releases it, then asks for IPv6 (requesting that it only handles IPv6 - but not bailing on errors in response), then tries to take IPv4 port again (on decent OSes, this should succeed).

The resulting build of somewhat simplified code works:

:; cat /etc/nut/upsd.conf
DEBUG_MIN 6
LISTEN *


:; journalctl -flu nut-server
...
Aug 14 13:47:15 pve nut-server[1631989]:    0.000241        [D1] debug level is '6'
Aug 14 13:47:15 pve nut-server[1631989]:    0.000254        [D3] setuptcp: try to bind to * port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000261        [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
Aug 14 13:47:15 pve nut-server[1631989]:    0.000265        [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
Aug 14 13:47:15 pve nut-server[1631989]:    0.000270        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000289        listening on 0.0.0.0 port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000295        [D3] setuptcp: Could bind to 0.0.0.0:3493 trying to handle a 'LISTEN *' directive; will release it for now to try IPv6
Aug 14 13:47:15 pve nut-server[1631989]:    0.000306        [D3] setuptcp: try to bind to ::0 port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000318        listening on ::0 port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000323        [D3] setuptcp: try taking IPv4 'ANY' again (if dual-stack IPv6 'ANY' did not grab it)
Aug 14 13:47:15 pve nut-server[1631989]:    0.000329        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000339        listening on 0.0.0.0 port 3493
Aug 14 13:47:15 pve nut-server[1631989]:    0.000344        [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'
Aug 14 13:47:15 pve nut-server[1631989]:    0.000349        [D3] setuptcp: also remembering IPv6 'ANY' instead of 'LISTEN *'
Aug 14 13:47:15 pve nut-server[1631989]:    0.000354        [D6] setuptcp: SKIP bind to ::0 port 3493: entry already initialized


:; netstat -anp | grep 3493
tcp        0      0 0.0.0.0:3493            0.0.0.0:*               LISTEN      1631989/upsd
tcp        0      0 127.0.0.1:51708         127.0.0.1:3493          ESTABLISHED 1631992/upsmon
tcp        0      0 127.0.0.1:3493          127.0.0.1:51708         ESTABLISHED 1631989/upsd
tcp6       0      0 :::3493                 :::*                    LISTEN      1631989/upsd

BTW, earlier discussions did not I think stress the SKIP bind to ::0 port 3493: entry already initialized bit: now that LISTEN * handling actively replaces the fields in an chained-list entry with the actual IP address used (instead of string asterisk) if one address family is in action, or injects another entry into the list if there are more address families, the setuptcp() code skips the new entry by checking that its FD is already valid.

@jimklimov
Copy link
Member Author

Finally, there were early ideas about checking if NUT was built with IPv6 support, or if the OS has IPV6_V6ONLY defined for setsockopt() -- seems these points were moot in fact. I did not find any precedent in current codebase to detect or optionally build for IPv4 and/or IPv6 only. The IPV6_V6ONLY token also seems resolved everywhere the NUT CI farm has its tentacles.

So if some platform would hiccup on these, it would likely need more massaging than an ifdef of one block.

@gdt
Copy link
Contributor

gdt commented Aug 14, 2023

I still think this is too complicated. We are asking for Vv6only=1, so it's a bug if that breaks v4. So just bind to v6 with v6only=1, and then bind to v4, and print an error if either fail, and be done with it. I don't think we have an example of where things go wrong and the sequence of try-4/release-4 is useful. There are too many lines of code in nut already, and we should be heading for fewer whenever possible.

@jimklimov
Copy link
Member Author

Yep, hence speaking of first iteration there. Taking small steps, all green :)

@jimklimov
Copy link
Member Author

FWIW, an artificial example (valid/invalid condition made broken) -- when one of the IP version ports IS in fact busy, but another works. Currently we do not define this as a failure - any successful port grab is okay (although we could... TOTHINK: if we wanted to get a port on an IPv6 AF but could not, and only got an IPv4 port - is it a fatal error?)

Aug 14 14:19:08 pve nut-server[2811168]:    0.000178        [D1] debug level is '6'
Aug 14 14:19:08 pve nut-server[2811168]:    0.000188        [D3] setuptcp: try to bind to * port 3493
Aug 14 14:19:08 pve nut-server[2811168]:    0.000192        [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
Aug 14 14:19:08 pve nut-server[2811168]:    0.000196        [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
Aug 14 14:19:08 pve nut-server[2811168]:    0.000199        [D3] setuptcp: try to bind to ::0 port 3493
Aug 14 14:19:08 pve nut-server[2811168]:    0.000216        listening on ::0 port 3493
Aug 14 14:19:08 pve nut-server[2811168]:    0.000220        [D3] setuptcp: Could not bind to ::0:3493 trying to handle a 'LISTEN *' directive
Aug 14 14:19:08 pve nut-server[2811168]:    0.000223        [D3] setuptcp: try taking IPv4 'ANY' (if dual-stack IPv6 'ANY' did not grab it)
Aug 14 14:19:08 pve nut-server[2811168]:    0.000227        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 14 14:19:08 pve nut-server[2811168]:    0.000235        listening on 0.0.0.0 port 3493
Aug 14 14:19:08 pve nut-server[2811168]:    0.000238        [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'

…o see if we can, then release it, then get IPv6 and then IPv4 again [networkupstools#2013 review for networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@jimklimov
Copy link
Member Author

Normal behavior for this iteration with both AFs active is now to just take IPv6 (only=1 if OS would), then try to take IPv4:

Aug 14 14:24:43 pve nut-server[3033934]:    0.000154        [D1] debug level is '6'
Aug 14 14:24:43 pve nut-server[3033934]:    0.000163        [D3] setuptcp: try to bind to * port 3493
Aug 14 14:24:43 pve nut-server[3033934]:    0.000166        [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
Aug 14 14:24:43 pve nut-server[3033934]:    0.000169        [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
Aug 14 14:24:43 pve nut-server[3033934]:    0.000172        [D3] setuptcp: try to bind to ::0 port 3493
Aug 14 14:24:43 pve nut-server[3033934]:    0.000186        listening on ::0 port 3493
Aug 14 14:24:43 pve nut-server[3033934]:    0.000189        [D3] setuptcp: try taking IPv4 'ANY' (if dual-stack IPv6 'ANY' did not grab it)
Aug 14 14:24:43 pve nut-server[3033934]:    0.000192        [D3] setuptcp: try to bind to 0.0.0.0 port 3493
Aug 14 14:24:43 pve nut-server[3033934]:    0.000199        listening on 0.0.0.0 port 3493
Aug 14 14:24:43 pve nut-server[3033934]:    0.000202        [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'
Aug 14 14:24:43 pve nut-server[3033934]:    0.000205        [D3] setuptcp: also remembering IPv6 'ANY' instead of 'LISTEN *'
Aug 14 14:24:43 pve nut-server[3033934]:    0.000209        [D6] setuptcp: SKIP bind to ::0 port 3493: entry already initialized

@jimklimov
Copy link
Member Author

For CLI options -4/-6 their request is honoured:

:; upsd -4
...
   0.000314     [D1] debug level is '6'
   0.000332     [D3] setuptcp: try to bind to * port 3493
   0.000340     [D1] setuptcp: handling 'LISTEN * 3493' with IPv4 any-address support
   0.000346     [D3] setuptcp: try taking IPv4 'ANY'
   0.000352     [D3] setuptcp: try to bind to 0.0.0.0 port 3493
   0.000377     listening on 0.0.0.0 port 3493
   0.000385     [D3] setuptcp: remembering IPv4 'ANY' instead of 'LISTEN *'
...

:; netstat -anp | grep 3493
tcp        0      0 0.0.0.0:3493            0.0.0.0:*               LISTEN      3182750/upsd
tcp        0      0 127.0.0.1:3493          127.0.0.1:54670         ESTABLISHED 3182750/upsd
tcp        0      0 127.0.0.1:54670         127.0.0.1:3493          ESTABLISHED 3033947/upsmon
:; upsd -6
...
   0.000313     [D1] debug level is '6'
   0.000327     [D3] setuptcp: try to bind to * port 3493
   0.000334     [D1] setuptcp: handling 'LISTEN * 3493' with IPv6 any-address support
   0.000343     [D3] setuptcp: try to bind to ::0 port 3493
   0.000370     listening on ::0 port 3493
   0.000383     [D3] setuptcp: remembering IPv6 'ANY' instead of 'LISTEN *'
...

:; netstat -anp | grep 3493
tcp6       0      0 :::3493                 :::*                    LISTEN      3205975/upsd

### Poor homeless `upsmon` now can not connect:

Aug 14 14:29:08 pve nut-monitor[3033947]: UPS [myups]: connect failed: Connection failure: Connection refused
Aug 14 14:29:13 pve nut-monitor[3033947]: UPS [myups]: connect failed: Connection failure: Connection refused
Aug 14 14:29:18 pve nut-monitor[3033947]: UPS [myups]: connect failed: Connection failure: Connection refused
...

…#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…h AF_INET and AF_INET6 (we have IPv4 and IPv6 capability everywhere, right?) [networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@gdt
Copy link
Contributor

gdt commented Aug 14, 2023

Yep, hence speaking of first iteration there. Taking small steps, all green :)

Sorry, didn't realize that. Party on!

…s to match current logic; drop GitHub reference [networkupstools#2013 review for networkupstools#2012]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
…NY" address listener conflict, consult actual state in "canhaveAnyV6" and not the possibility via "serverAnyV6!=null" [networkupstools#2013]

Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
@jimklimov jimklimov added ready / code review Author (and CI) consider the PR worthy of human rewievers' time ready / gonna merge The PR is in final cycles leading to merge unless someone logs an objection before we hit the button labels Aug 14, 2023
@jimklimov
Copy link
Member Author

So, hopefully the small hackfest is over and the changeset is good to go? ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation enhancement ready / code review Author (and CI) consider the PR worthy of human rewievers' time ready / gonna merge The PR is in final cycles leading to merge unless someone logs an objection before we hit the button service/daemon start/stop General subject for starting and stopping NUT daemons (drivers, server, monitor); also BG/FG/Debug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LISTEN * for listening with upsd on "any address" does not work with both IPv4 and IPv6

2 participants