-
Notifications
You must be signed in to change notification settings - Fork 1.3k
agetty: Implement netlink based IP processing #3649
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
base: master
Are you sure you want to change the base?
Conversation
1b5a247
to
1ddc848
Compare
else | ||
{ | ||
/* There can be race, we do not return error here */ | ||
/* FIXME I18N: *"unknown"* is too generic. Use context. */ |
Check notice
Code scanning / CodeQL
FIXME comment Note
* That is why again label is here: netlink_groups will be re-evaluated and | ||
* dump will be performed again. | ||
*/ | ||
/* netlink_groups = 0; */ |
Check notice
Code scanning / CodeQL
Commented-out code Note
} | ||
|
||
#define DBG_CASE(x) case x: str = #x; break | ||
#define DBG_CASE_DEF8(x) default: snprintf(strx+2, 3, "%02hhx", x); str = strx; break |
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.
This is a bit confusing; the DBG_
prefix seems related to the debug.h stuff, but it's function to convert switch-case to a string. Maybe rename to CASE_TO_STRING() or so.
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'd also suggest using str
(and strx
) as an argument for the macro rather than hiding any variable use in the macros. The strx+2 looks unnecessary, as you can use "0x" in the snprintf() (just "0x%02hhx").
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.
It is a part of a debug-only code.
The macro purpose is just a simplification of the repeated pieces of the code that cannot be covered by a function.
str and strx as a parameter would make more sense in case if we want to move this code to debug.h. In this single use case it would again create repeated parts in the code.
strx+2 just saved copying 2 bytes on each run (at cost of worse readability).
#define DBG_CASE(x) case x: str = #x; break | ||
#define DBG_CASE_DEF8(x) default: snprintf(strx+2, 3, "%02hhx", x); str = strx; break | ||
static char *ip_rating(enum ul_netaddrq_ip_rating q) | ||
{ |
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.
Maybe rename to ip_rating_as_string() to make it more readable..
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.
Changing.
lib/netaddrq.c
Outdated
{ | ||
DBG(LIST, ul_debugobj(addrq, | ||
"malloc() 1 failed")); | ||
return -1; |
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.
return -ENOMEM;
to be compatible with usual util-linux code patterns/
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.
and maybe the DBG() is overkill in this rare case :-)
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.
Changing.
free(newaddr); | ||
error1: | ||
return NULL; | ||
} |
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.
Do we need all the errorN gotos there? If you use calloc(), then you can use
error:
if (newaddr) {
free(newaddr->ifa_local);
free(newaddr->ifa_address);
free(newaddr);
}
return NULL;
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.
or better:
error:
ul_nl_addr_free(newaddr);
return NULL;
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.
Changing.b
DBG(LIST, ul_debugobj(addrq, | ||
"ul_nl_addr_dup() failed")); | ||
rc = -1; | ||
goto error; |
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.
rc = -ENOMEM;
as ul_nl_addr_dup() can fail only on memory allocation.
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.
Changing and again removing DBG here and in similar cases.
free(addr->ifa_local); | ||
free(addr->ifname); | ||
free(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.
It's better when free()-like functions are robust enough to accept NULL as an argument (like libc free()). It means
void ul_nl_addr_free (struct ul_nl_addr *addr)
{
if (addr) {
free(addr->ifa_address);
free(addr->ifa_local);
free(addr->ifname);
free(addr);
}
}
Then you can call the function in an arbitrary situation, including error cases, when you need to clean up anything.
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.
Changing.
lib/netaddrq.c
Outdated
|
||
netaddrq_init_debug(); | ||
if (!(nl->data_addr = malloc(sizeof(struct ul_netaddrq_data)))) | ||
return -1; |
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.
-ENOMEM; :-)
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 would also suggest using calloc() to ensure greater robustness for future changes to the struct. This is a generic recommendation when working with structs in an interface, especially if you do not plan to use a function like memcpy() after malloc().
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.
Changing and removing explicit zeroing.
lib/netaddrq.c
Outdated
*threshold = ul_netaddrq_bestaddr(nl, best_ifaceq, &best, ifa_family); | ||
if (best[*threshold]) | ||
return ul_nl_addr_ntop_address(best[*threshold]->addr); | ||
else |
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.
please, no else-after-return ;-)
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.
Ok.
abbc025
to
a007eab
Compare
addrq = UL_NETADDRQ_DATA(nl); | ||
addrq->callback_pre = callback_pre; | ||
addrq->callback_post = callback_post; | ||
addrq->callback_data = data; |
Check warning
Code scanning / CodeQL
Local variable address stored in non-local memory Warning
parameter
lib/netlink.c
Outdated
|
||
int ul_nl_process(struct ul_nl_data *nl, bool async, bool loop) | ||
{ | ||
char buf[4096]; |
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.
4096 -> BUFSIZ ?
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, there is no exact size defined. If we don't want to handle MSG_TRUNC, it should cover sizeof (struct nlmsghdr) plus the expected message payload size. BUFSIZ (8192) is surely more safe than 4096.
term-utils/agetty.c
Outdated
/* In case of any error, the addrq list is just empty, and we can use | ||
* the code without any error checking. */ | ||
ul_nl_close(&(ie->nl)); | ||
ie->nl.fd = 0; |
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.
Should be there ie->nl.fd = -1;
?
term-utils/agetty.c
Outdated
netlink_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; | ||
|
||
/* Already initialized? */ | ||
if (ie->nl.fd) |
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 if (ie->nl.fd >= 0)
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 code was indeed intended to represent 0 as no fd opened, supposing that fd channel 0 is never used for the netlink socket. But not fully, select() uses 0 as an active value. -1 is for sure safer and the more convenient value.
Updating code in the comments as well.
And actually, it needs to update the initialization in show_issue to:
struct issue ie = { .output = NULL, .nl.fd = -1 };
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.
And, actually, also another struct issue initialization needs update as well:
struct issue issue = {
.mem = NULL,
.nl.fd = -1
};
To support netlink and IP address processing, two new library files were added: netlink: Generic netlink message processing code converting netlink messages to calls of callbacks with a pre-processed data. netaddrq: A code that gets and maintains linked list of the current interfaces and assigned IP addresses. It also provides a rating of IP addresses based on its "quality", i. e. type of address, validity, lifetime etc. Signed-off-by: Stanislav Brabec <[email protected]>
a007eab
to
99a946b
Compare
The current \4 and \6 issue file escapes implementation is inferior. It uses get getifaddrs() to get a list of IP addresses. This function does not provide enough information to discriminate between stable IP addresses and ephemeral addresses. As a result, especially \6 often gives unreliable results. The code is actually unable to get list of all interfaces, so a proper out of the box IP address reporting depends on external tools that generate issue file with the interfaces list. The netlink messages are already used, but only as a change notifier. The contents is not used, even if it contains exact information about the change. As a result, change processing is triggered even for unrelated network changes like IPv6 router advertisement. The new implementation uses the new netaddrq library. It reports more reliable results especially for IPv6. Additionally, two new escapes are implemented: \a Report all interfaces and assigned addresses that are considered as reliable. \A Report all interfaces and all assigned addresses. TODO: To prevent overflooding of the console, the list is currently limited to 12 interfaces. It would be nice to make it configurable. Two pass processing of issue files. First pass just collects IP protocols and list of interfaces (in future interface patterns). Now it always processes both IPv4 and IPv6 on all interfaces. Not so bad, as \a is smart enough to display just the useful part. Maybe implement more options and formatting support for \a and \A. Maybe implement interface filter globs or regexps for \a and \A. Still not so bad, as \a automatically skips interfaces without reliable addresses (e. g. lo or TUN). Signed-off-by: Stanislav Brabec <[email protected]>
99a946b
to
ad3b86d
Compare
Looks good to me (with minimal netlink experience). I think we can merge it and continue with possible improvements within the tree. The following significant change will be to modify agetty to keep parsed issue files in memory (two-pass processing). @t-8ch do you want to review? |
https://build.opensuse.org/request/show/1303156 by user sbrabec + anag_factory - Implement escape code for printing of ssh host keys in agetty issue file (util-linux-agetty-ssh-host-keys.patch. - Include fixes from util-linux/util-linux#3649 (jsc#PED-8734, util-linux-lib-netlink.patch, util-linux-agetty-netlink.patch).
The current \4 and \6 issue file escapes implementation is inferior. It uses get getifaddrs() to get a list of IP addresses. This function does not provide enough information to discriminate between stable IP addresses and ephemeral addresses. As a result, especially \6 often gives unreliable results. This is a limitation of getifaddrs().
This change set implement netlink base IP address processing in agetty, and creates a library for generic use. It solves the problem and provides a generic framework for the future use.
TODO:
treewide:
There are more places using netlink. It would be nice to use the library in all these places.
agetty:
Two pass processing of issue files. First pass just collects IP protocols and list of interfaces (in future interface patterns). Now it always processes both IPv4 and IPv6 on all interfaces. Not so bad, as \a is smart enough to display just the useful part.
Maybe implement more options and formatting support for \a and \A.
netaddrq:
callback_pre implementing interface filters.
Support batch processing in the monitor mode. Sometimes the processing delays and more messages are ready at once. Now each message is processed separately. It would be nice to support a mode, where only the list update is performed for each message, but all changes are then released at once (e. g. in case of agetty, only one reload can appear, if both IPv4 and IPv6 addresses appear at once).
Configurable limit of interfaces. To prevent screen overflowing, it is currently hardcoded as 12.
There is a small regression related to it. If your machine has more interfaces, then the processing is stopped. As a result, even \4 and \6 could return an empty string, if the requested interface is, say 1000th in order.
FIXME:
The implementation found a problem with inferior I18N support in util-linux. A context needs to be added to words like "unknown", otherwise translation to some languages cannot be done properly, as "unknown" or "(unknown)" may need different translation in particular contexts.