Re: [PATCH] agetty: Implement netlink based IP address lookup

Karel Zak <[email protected]>
Newsgroups org.kernel.vger.util-linux
Message-ID <tnzflf25amkje3xdgf7avon5smn3xlbng44xxegwpsjjqp4tvy@4vfjbliaoiq3>
On Sun, Feb 16, 2025 at 10:24:50PM GMT, Stanislav Brabec wrote:
> The new netlink based IP address lokup implements a new issue file escape \i.
> This lookup is based on netlink messages only. It evaluates all addresses
> reported in the netlink messages and it attempts to print only the best of them.
> As it is not possible to discriminate the "best" IP address, it reports all
> reasonably usable addresses.

I believe your code is a good start.

> Migrate \4 and \6 from getifaddrs() to netlink as well, and then completely drop
> getifaddrs() based code.

Yes, but it would be nice to make the code more readable and extendable :-)

My suggestion:

- Move the code to lib/netlink-ifaces.c.

- Add #ifdef TEST_PROGRAM with test main() and test_netlink to
  lib/Makemodule.am (see for example lib/linux_version.c).

- Use include/debug.h and UL_NETLINK_DEBUG= environment variable (see
  lib/loopdev.c, LOOPDEV_DEBUG_* and loopdev_init_debug()).

- Use a prefix for public functions (e.g., ul_iface_*).

- Create a set of small functions for basic operations:
  - Add and remove interface (e.g., add_interface()).
  - Add and remove address.
  - Get the best address.

I also suggest using include/list.h for the lists, but you might find
it challenging at first. Ideally, define next() functions for while()
loops (e.g., ul_iface_next_interface() and ul_iface_next_addr()). See
for example libfdisk/src/table.c:fdisk_table_next_partition().
However, I'll understand if you think it's overkill.

The include/list.h provides a sort function, so it would be possible
to sort the lists by "quality", making the first address in the list
the best.

> It could be useful to implement:
> - configurable limit for the maximum number of interfaces that could be
>   reported.

I believe the limit could be quite large by default (512?) since it's
just one malloc() for each address.

> - the interface regexp match

Yes, just another small function ul_iface_match_interface(), and if it
will be sorted than you get the best one :-)

\4{~eth[0-9]} or \4{match="~eth[0-9]"} if you want to support more
options in the { }.

 - agetty already supports \foo{ } arguments, see get_escape_argument()
 - lib/strutils.c:ul_optstr_next() can parse options list  {a="aaa",b="bbb"}

> - an interface template for fine tuning of the output

You can introduce printf()-like \4{format="%iface:%addr} (like git-log)

> - better control over the output: e. g. disable printing of IPv4 or IPv6
>   addresses, disable printing of temporary addresses or link-local addresses
> - cross-protocol checks: e. g. disable reporting of link-local IPv6 address for
>   an IPv4-only interface

:-)

> The new network comparison code could potentially render obsolete string based
> issue comparison (implemented in 6522d88). There are ifaces_list_change_4 and
> ifaces_list_change_6 variables ready for this purpose.

Hmm... the global variables used deeply within the netlink message
parser do not look very elegant. Maybe introduce a separate struct,
ul_ifaces_status, and use it as a global variable in agetty.c,
then call process_netlink_msg() with this struct as an argument.

> +struct ip_quality_item {
> +	enum ip_quality_item_value quality;
> +	int len;
> +	__u32 ifa_valid; /* IP addres lifetime */

uint32_t if possible ;-)

> +	struct ip_quality_item *next;
> +	char addr[];
> +};
> +struct iface_quality_item {
> +	__u32 ifa_index;
> +	struct ip_quality_item *ip_quality_list_4;
> +	struct ip_quality_item *ip_quality_list_6;
> +	struct iface_quality_item *next;
> +};
> +static struct iface_quality_item *ifaces_list = NULL;
> +bool ifaces_list_change_4;
> +bool ifaces_list_change_6;
> +bool ifaces_skip_dump = false;

Do we need the _quality_ in the sctruct names? What about ul_iface and
ul_iface_addr ?

 ...

> +			debug_net("+ allocating new interface\n");
> +			ifaceq = malloc(sizeof(struct iface_quality_item));

 if (!ifaceq)
    return;

> +			ifaceq->ifa_index = ifaddr->ifa_index;
> +			ifaceq->ip_quality_list_4 = NULL;
> +			ifaceq->ip_quality_list_6 = NULL;
> +			ifaceq->next = NULL;
> +			*ifaceq_prev_next = ifaceq;


process_netlink_msg_part() will be simpler and more readable if you
move the basic tasks to small functions like add_interface() :-)

> +		} else {
> +			/* Should never happen */
> +			debug_net("- interface not found\n");
> +			return;
> +		}
> +	}
> +	if (family == AF_INET) {
> +		ipq_prev_next = &(ifaceq->ip_quality_list_4);
> +		ifaces_list_change = &ifaces_list_change_4;
> +	}
> +	if (family == AF_INET6) {
> +		ipq_prev_next = &(ifaceq->ip_quality_list_6);
> +		ifaces_list_change = &ifaces_list_change_6;
> +	}

I guess static analyzers won't like this part; ipq_prev_next is
uninitialized if the family is something else.

 
    Karel

-- 
 Karel Zak  <[email protected]>
 http://karelzak.blogspot.com
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.