Network boot: ignore interfaces without a carrier

Also skip interfaces which can't be brought up for some reason
(i.e. missing firmware). Thus the system can boot automatically
when exactly one interface has a carrier.

Closes: #40616
This commit is contained in:
Alexey Sheplyakov 2021-08-02 03:49:42 +04:00
parent dd58d27275
commit 110ac7dff1

View File

@ -269,6 +269,38 @@ void get_medias(enum media_type media, char *** names, char *** models)
#endif /* DISABLE_MEDIAS */
#ifndef DISABLE_NETWORK
static int net_device_has_carrier(const char *device) {
int fd = -1;
char *p = NULL;
int ret = 0;
char state = '\0';
if (asprintf(&p, "/sys/class/net/%s/carrier", device) == -1) {
log_message("net_device_has_carrier: OOM");
return 0;
}
fd = open(p, O_RDONLY);
if (fd < 0) {
log_perror("net_device_has_carrier: failed to open sysfs knob");
goto out;
}
if (read(fd, &state, 1) != 1) {
log_perror("net_device_has_carrier: failed to read sysfs knob");
goto out;
}
ret = !!(state == '1');
out:
if (p) {
free(p);
p = NULL;
}
if (fd >= 0) {
close(fd);
fd = -1;
}
return ret;
}
static int net_device_available(char * device)
{
struct ifreq req;
@ -285,8 +317,37 @@ static int net_device_available(char * device)
close(s);
return 0;
}
if (!(req.ifr_flags & IFF_UP)) {
req.ifr_flags |= IFF_UP;
if (ioctl(s, SIOCSIFFLAGS, &req)) {
log_perror("ifup");
/* skip the interface we can't bring up */
}
/* XXX: we want to report only interfaces with carrier as available.
* However calling net_device_has_carrier right after ifup is useless:
* the kernel needs some time to figure out if the carrier is present.
* Therefore report that this interface is *not* available.
* intf_select_and_up will wait a second and retry if necessary
* (i.e. no interfaces with carrier have been found), and on the next
* attempt `device` will be already up, and we'll check if `device`
* has the carrier.
*
* Sounds a bit tricky? A simple solution is to put sleep(1) after
* ifup, but there might be quite a number of interfaces (I've got
* boards with 4 and 6 Ethernets), and sleeping for O(N) seconds
* for no good reason might be annoying.
* Oh, alternatively we could setup a full-fledged event loop and
* wait for netlink notifications, but that's too much code and
* *complexity*.
*/
close(s);
return 0;
}
/* `device` was already up (perhaps we've brought it up during
* previous calls), check if it has the carrier
*/
close(s);
return 1;
return net_device_has_carrier(device);
}