Skip to content
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

Teach nut-scanner to handle several IP address ranges #2509

Merged
merged 17 commits into from
Jul 8, 2024

Conversation

jimklimov
Copy link
Member

@jimklimov jimklimov commented Jul 4, 2024

Now the -m cidr and -s/-e option groups can be repeated on command line, and cause several IP address ranges to be scanned for SNMP, NetXML, IPMI and old NUT buses.

Preparation for #2244 (-m auto which would likely find several connected subnets on a typical server). That step would likely need something like:

Also may need better support for CIDR netmasks (not bit counts), and larger masks for IPv6.

Review and practical testing in an environment with many devices to see would be welcome.

@jimklimov jimklimov added enhancement need testing Code looks reasonable, but the feature would better be tested against hardware or OSes nut-scanner impacts-release-2.8.2 Issues reported against NUT release 2.8.2 (maybe vanilla or with minor packaging tweaks) labels Jul 4, 2024
@jimklimov jimklimov added this to the 2.8.3 milestone Jul 4, 2024
…}: report the IP address (range, single, none) in the log [networkupstools#2244]

Signed-off-by: Jim Klimov <[email protected]>
… options and others behave differently without them [networkupstools#2244]

Signed-off-by: Jim Klimov <[email protected]>
… introduce a way to scan several IP address ranges [networkupstools#2244]

Signed-off-by: Jim Klimov <[email protected]>
…ice() from adding a list to itself

Signed-off-by: Jim Klimov <[email protected]>
…etworkupstools#2244]

We actually use DEFAULT_NETWORK_TIMEOUT from common.h
same as in clients/upsclient.c

Signed-off-by: Jim Klimov <[email protected]>
@jimklimov jimklimov force-pushed the issue-2244 branch 2 times, most recently from a4cbceb to c267ed1 Compare July 5, 2024 03:27
@jimklimov
Copy link
Member Author

jimklimov commented Jul 5, 2024

In a recent commit, added the code to getifaddrs() and report what we would pass further as IP address ranges, count the netmask bits, etc.

The final code would go beyond this PoC reporting of course; the primary goal of this push is to see how many platforms would complain about unknown methods and data types :)

Like hitting https://stackoverflow.com/questions/35551879/cast-from-sockaddr-to-sockaddr-in-increases-required-alignment ...

@AppVeyorBot
Copy link

@jimklimov
Copy link
Member Author

From messages seen so far, the code is more portable than I expected, at least across POSIX systems.

  • One issue is about different alignment of sockaddr variants, as highlighted by clang builds:
nut-scanner.c:755:37: error: cast from 'struct sockaddr *' to 'struct sockaddr_in6 *' increases required alignment from 1 to 4 [-Werror,-Wcast-align]
                                                                                struct sockaddr_in6 *sm = (struct sockaddr_in6 *)ifa->ifa_netmask;
                                                                                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nut-scanner.c:769:36: error: cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 1 to 4 [-Werror,-Wcast-align]
                                                                                struct sockaddr_in *sa = (struct sockaddr_in *)ifa->ifa_addr;
                                                                                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nut-scanner.c:770:36: error: cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 1 to 4 [-Werror,-Wcast-align]
                                                                                struct sockaddr_in *sm = (struct sockaddr_in *)ifa->ifa_netmask;
                                                                                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
  • Another is about different integer word sizes for the flags variable and formatting string for the print-out (64-bit on illumos builds, so not an int-sized %x):
nut-scanner.c: In function 'main':
nut-scanner.c:767:77: error: format '%x' expects argument of type 'unsigned int', but argument 6 has type 'uint64_t' {aka 'long unsigned int'} [-Werror=format=]
  767 |           printf("Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08x", ifa->ifa_name, addr, mask, masklen, ifa->ifa_flags);
      |                                                                          ~~~^                                       ~~~~~~~~~~~~~~
      |                                                                             |                                          |
      |                                                                             unsigned int                               uint64_t {aka long unsigned int}
      |                                                                          %08lx

nut-scanner.c:780:77: error: format '%x' expects argument of type 'unsigned int', but argument 6 has type 'uint64_t' {aka 'long unsigned int'} [-Werror=format=]
  780 |           printf("Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08x", ifa->ifa_name, addr, mask, masklen, ifa->ifa_flags);
      |                                                                          ~~~^                                       ~~~~~~~~~~~~~~
      |                                                                             |                                          |
      |                                                                             unsigned int                               uint64_t {aka long unsigned int}
      |                                                                          %08lx
  • And so far little substantial feedback from Windows:
nut-scanner.c:44:10: fatal error: arpa/inet.h: No such file or directory
   44 | #include <arpa/inet.h>
      |          ^~~~~~~~~~~~~
compilation terminated.

Build scenarios are still running, so maybe something else would crop up. But I can start addressing these already, to leave fewer raised alarms in the next iteration...

@jimklimov
Copy link
Member Author

jimklimov commented Jul 5, 2024

Log from a test run, about parsing different addresses (e.g. -s followed by another -s ends up as two individual addresses):

:; make -ks -j && ./tools/nut-scanner/nut-scanner -D -s 1.2.3.4 -s 1.1.1.1 -e 1.1.1.3 -m 10.94.56.0/24 -m 127.0.0.0/28 -O -m auto
...
   0.090346     [D5] add_ip_range: only start address was provided, setting end to same: 1.2.3.4
   0.090373     [D1] Recorded IP address range #1: [1.2.3.4 .. 1.2.3.4]
   0.090399     [D1] Recorded IP address range #2: [1.1.1.1 .. 1.1.1.3]
   0.090426     [D5] Processing CIDR net/mask: 10.94.56.0/24
   0.090454     [D5] nutscan_cidr_to_ip: parsed cidr=10.94.56.0/24 into first_ip=10.94.56.0 and mask=24
   0.090482     [D5] nutscan_cidr_to_ip: parsed mask value 24
   0.090529     [D5] Extracted IP address range from CIDR net/mask: 10.94.56.0 => 10.94.56.255
   0.090556     [D1] Recorded IP address range #3: [10.94.56.0 .. 10.94.56.255]
   0.090581     [D5] Processing CIDR net/mask: 127.0.0.0/28
   0.090589     [D5] nutscan_cidr_to_ip: parsed cidr=127.0.0.0/28 into first_ip=127.0.0.0 and mask=28
   0.090611     [D5] nutscan_cidr_to_ip: parsed mask value 28
   0.090638     [D5] Extracted IP address range from CIDR net/mask: 127.0.0.0 => 127.0.0.15
   0.090663     [D1] Recorded IP address range #4: [127.0.0.0 .. 127.0.0.15]
   0.090779     [D5] Discovering getifaddrs(): Interface: lo    Address: 255.0.0.0      Mask: 255.0.0.0 (len: 8)        Flags: 00010049 IFF_LOOPBACK IFF_UP IFF_RUNNING
   0.090808     [D5] Discovering getifaddrs(): Interface: eth0  Address: 255.255.240.0  Mask: 255.255.240.0 (len: 20)   Flags: 00011043 IFF_UP IFF_RUNNING IFF_BROADCAST(is assigned)
   0.090837     [D5] Discovering getifaddrs(): Interface: eth0  Address: 255.255.240.0  Mask: 255.255.240.0 (len: 20)   Flags: 00011043 IFF_UP IFF_RUNNING IFF_BROADCAST(is assigned)
   0.090866     [D5] Discovering getifaddrs(): Interface: lo    Address: ::1    Mask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff (len: 128)        Flags: 00010049 IFF_LOOPBACK IFF_UP IFF_RUNNING
   0.090982     [D5] Discovering getifaddrs(): Interface: eth0  Address: fe80::215:5dff:fe8b:d950%eth0  Mask: ffff:ffff:ffff:ffff:: (len: 64)   Flags: 00011043 IFF_UP IFF_RUNNING IFF_BROADCAST(is assigned)
...

@jimklimov
Copy link
Member Author

Added -m auto logic for actually converting discovered subnets into CIDR and adding to IP ranges for scanning. Now the log looks like:

   0.082536     [D1] nutscan_init: failed to load the library for NUT Client library
   0.082553     [D5] add_ip_range: only start address was provided, setting end to same: 1.2.3.4
   0.082563     [D1] Recorded IP address range #1: [1.2.3.4 .. 1.2.3.4]
   0.082593     [D1] Recorded IP address range #2: [1.1.1.1 .. 1.1.1.3]
   0.082602     [D5] Processing CIDR net/mask: 10.94.56.0/24
   0.082616     [D5] nutscan_cidr_to_ip: parsed cidr=10.94.56.0/24 into first_ip=10.94.56.0 and mask=24
   0.082644     [D5] nutscan_cidr_to_ip: parsed mask value 24
   0.082673     [D5] Extracted IP address range from CIDR net/mask: 10.94.56.0 => 10.94.56.255
   0.082718     [D1] Recorded IP address range #3: [10.94.56.0 .. 10.94.56.255]
   0.082724     [D5] Processing CIDR net/mask: 127.0.0.0/28
   0.082733     [D5] nutscan_cidr_to_ip: parsed cidr=127.0.0.0/28 into first_ip=127.0.0.0 and mask=28
   0.082738     [D5] nutscan_cidr_to_ip: parsed mask value 28
   0.082746     [D5] Extracted IP address range from CIDR net/mask: 127.0.0.0 => 127.0.0.15
   0.082750     [D1] Recorded IP address range #4: [127.0.0.0 .. 127.0.0.15]
   0.082899     [D5] Discovering getifaddrs(): Interface: lo    Address: 127.0.0.1      Mask: 255.0.0.0 (len: 8)        Flags: 00010049 IFF_LOOPBACK IFF_UP IFF_RUNNING
   0.082938     [D5] Discovering getifaddrs(): Interface: eth0  Address: 172.28.79.185  Mask: 255.255.240.0 (len: 20)   Flags: 00011043 IFF_UP IFF_RUNNING IFF_BROADCAST(is assigned)
   0.082948     [D5] Processing CIDR net/mask: 172.28.79.185/20
   0.082957     [D5] nutscan_cidr_to_ip: parsed cidr=172.28.79.185/20 into first_ip=172.28.79.185 and mask=20
   0.082961     [D5] nutscan_cidr_to_ip: parsed mask value 20
   0.082967     [D5] Extracted IP address range from CIDR net/mask: 172.28.64.0 => 172.28.79.255
   0.082971     [D1] Recorded IP address range #5: [172.28.64.0 .. 172.28.79.255]
   0.082977     [D5] Discovering getifaddrs(): Interface: eth0  Address: 172.22.48.99   Mask: 255.255.240.0 (len: 20)   Flags: 00011043 IFF_UP IFF_RUNNING IFF_BROADCAST(is assigned)
   0.082981     [D5] Processing CIDR net/mask: 172.22.48.99/20
   0.082984     [D5] nutscan_cidr_to_ip: parsed cidr=172.22.48.99/20 into first_ip=172.22.48.99 and mask=20
   0.082987     [D5] nutscan_cidr_to_ip: parsed mask value 20
   0.083017     [D5] Extracted IP address range from CIDR net/mask: 172.22.48.0 => 172.22.63.255
   0.083023     [D1] Recorded IP address range #6: [172.22.48.0 .. 172.22.63.255]
   0.083037     [D5] Discovering getifaddrs(): Interface: lo    Address: ::1    Mask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff (len: 128)        Flags: 00010049 IFF_LOOPBACK IFF_UP IFF_RUNNING
   0.083121     [D5] Discovering getifaddrs(): Interface: eth0  Address: fe80::215:5dff:fe8b:d950%eth0  Mask: ffff:ffff:ffff:ffff:: (len: 64)   Flags: 00011043 IFF_UP IFF_RUNNING IFF_BROADCAST(is assigned)
   0.083188     [D5] Processing CIDR net/mask: fe80::215:5dff:fe8b:d950%eth0/64
   0.083195     [D5] nutscan_cidr_to_ip: parsed cidr=fe80::215:5dff:fe8b:d950%eth0/64 into first_ip=fe80::215:5dff:fe8b:d950%eth0 and mask=64
   0.083203     [D5] nutscan_cidr_to_ip: parsed mask value 64
   0.083211     [D5] nutscan_cidr_to_ip: getaddrinfo() failed for AF_INET (IPv4): -9
   0.083271     [D5] Extracted IP address range from CIDR net/mask: fe80:: => fe80::ffff:ffff:ffff:ffff
   0.083314     [D1] Recorded IP address range #7: [fe80:: .. fe80::ffff:ffff:ffff:ffff]

Need to chop off %interface from IPv6 addresses (or ignore it in nutscan_cidr_to_ip; probably move up from 32-bit mask assumption too).

@jimklimov
Copy link
Member Author

jimklimov commented Jul 5, 2024

Aha, not a big deal: [D5] nutscan_cidr_to_ip: getaddrinfo() failed for AF_INET (IPv4, will retry with IPv6): -9: Address family for hostname not supported

@jimklimov
Copy link
Member Author

jimklimov commented Jul 5, 2024

@aquette : link-local addresses (unicast ones like fe80::/10 or 169.254.0.0/16, or multicast ones) can be a problem by default, ordering millions or billions of scans. Do we want to impose some limits? Even a lazy org with 10.0.0.0/8 distributed over DHCP can be a problem :D

https://en.wikipedia.org/wiki/Link-local_address

Unfortunately, there does not seem to be a portable flag for those, e.g. https://man7.org/linux/man-pages/man7/netdevice.7.html -- any ideas?

… and logged reports to help troubleshooting [networkupstools#2244]

Signed-off-by: Jim Klimov <[email protected]>
… in the name, and use static structs and memcpy() to actually fix alignment warnings [networkupstools#2244]

Signed-off-by: Jim Klimov <[email protected]>
… actually fix alignment warnings

Signed-off-by: Jim Klimov <[email protected]>
@jimklimov
Copy link
Member Author

jimklimov commented Jul 5, 2024

Ultimately, separating the -m auto experiments from an already achieved and hopefully portable increment of ability to scan multiple IP ranges, and application of some lessons-learned (e.g. avoidance of alignment problems rather than going hush-hush and sweeping them under the rug. => the rest offloaded to #2513

@AppVeyorBot
Copy link

@jimklimov jimklimov merged commit 7b26627 into networkupstools:master Jul 8, 2024
30 checks passed
@jimklimov jimklimov deleted the issue-2244 branch July 8, 2024 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement impacts-release-2.8.2 Issues reported against NUT release 2.8.2 (maybe vanilla or with minor packaging tweaks) need testing Code looks reasonable, but the feature would better be tested against hardware or OSes nut-scanner
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants