summaryrefslogtreecommitdiffstats
path: root/discover/udev.c
diff options
context:
space:
mode:
authorSamuel Mendoza-Jonas <sam@mendozajonas.com>2017-06-15 15:23:06 +1000
committerSamuel Mendoza-Jonas <sam@mendozajonas.com>2017-07-11 14:50:00 +1000
commit58db060fbb1548a0acdfc475fa41fe86fb32dd11 (patch)
treebf23d028b13d7f5d9f694d2e2455fc8a9a2f64e6 /discover/udev.c
parent515d2f03bae8d5617ee3bce5a46287203f7215c2 (diff)
downloadtalos-petitboot-58db060fbb1548a0acdfc475fa41fe86fb32dd11.tar.gz
talos-petitboot-58db060fbb1548a0acdfc475fa41fe86fb32dd11.zip
discover: Wait for net interfaces to be marked ready
If pb-discover is started before udev has settled there is a race between Petitboot configuring interfaces and udev renaming them. If an interface is set "up" the name change will fail and interfaces can be inconsistently named, eg: Device: (*) eth0 [0c:c4:7a:f4:1c:50, link up] ( ) enP1p9s0f1 [0c:c4:7a:f4:1c:51, link down] ( ) enP1p9s0f2 [0c:c4:7a:f4:1c:52, link down] ( ) enP1p9s0f3 [0c:c4:7a:f4:1c:53, link down] Add "net" devices to the udev filter and wait for them to be announced by udev before configuring them. udev_enumerate_add_match_is_initialized() ensures that by the time an interface appears via udev its name will be consistent. This also swaps the network and udev init order, but since interfaces now will not be configured until after udev is ready this should not have a user-visible effect. Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
Diffstat (limited to 'discover/udev.c')
-rw-r--r--discover/udev.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/discover/udev.c b/discover/udev.c
index f4754b1..45a8b56 100644
--- a/discover/udev.c
+++ b/discover/udev.c
@@ -27,6 +27,7 @@
#include "device-handler.h"
#include "cdrom.h"
#include "devmapper.h"
+#include "network.h"
/* We set a default monitor buffer size, as we may not process monitor
* events while performing device discvoery. systemd uses a 128M buffer, so
@@ -230,6 +231,69 @@ static int udev_handle_block_add(struct pb_udev *udev, struct udev_device *dev,
return 0;
}
+/*
+ * Mark valid interfaces as 'ready'.
+ * The udev_enumerate_add_match_is_initialized() filter in udev_enumerate()
+ * ensures that any device we see is properly initialized by udev (eg. interface
+ * names); here we check that the properties are sane and mark the interface
+ * as ready for configuration in discover/network.
+ */
+static int udev_check_interface_ready(struct device_handler *handler,
+ struct udev_device *dev)
+{
+ const char *name, *name_path, *ifindex, *interface, *mac_name;
+ uint8_t *mac;
+ char byte[3];
+ unsigned int i, j;
+
+
+ name = udev_device_get_sysname(dev);
+ if (!name) {
+ pb_debug("udev_device_get_sysname failed\n");
+ return -1;
+ }
+
+ name_path = udev_device_get_property_value(dev, "ID_NET_NAME_PATH");
+ ifindex = udev_device_get_property_value(dev, "IFINDEX");
+ interface = udev_device_get_property_value(dev, "INTERFACE");
+ mac_name = udev_device_get_property_value(dev, "ID_NET_NAME_MAC");
+
+ /* Physical interfaces should have all of these properties */
+ if (!name_path || !ifindex || !interface || !mac_name) {
+ pb_debug("%s: interface %s missing properties\n",
+ __func__, name);
+ return -1;
+ }
+
+ /* ID_NET_NAME_MAC format is enxMACADDR */
+ if (strlen(mac_name) < 15) {
+ pb_debug("%s: Unexpected MAC format: %s\n",
+ __func__, mac_name);
+ return -1;
+ }
+
+ mac = talloc_array(handler, uint8_t, HWADDR_SIZE);
+ if (!mac)
+ return -1;
+
+ /*
+ * ID_NET_NAME_MAC is not a conventionally formatted MAC
+ * string - convert it before passing it to network.c
+ */
+ byte[2] = '\0';
+ for (i = strlen("enx"), j = 0;
+ i < strlen(mac_name) && j < HWADDR_SIZE; i += 2) {
+ memcpy(byte, &mac_name[i], 2);
+ mac[j++] = strtoul(byte, NULL, 16);
+ }
+
+ network_mark_interface_ready(handler,
+ atoi(ifindex), interface, mac, HWADDR_SIZE);
+
+ talloc_free(mac);
+ return 0;
+}
+
static int udev_handle_dev_add(struct pb_udev *udev, struct udev_device *dev)
{
const char *subsys;
@@ -247,6 +311,10 @@ static int udev_handle_dev_add(struct pb_udev *udev, struct udev_device *dev)
return -1;
}
+ /* If we see a net device, check if it is ready to be used */
+ if (!strncmp(subsys, "net", strlen("net")))
+ return udev_check_interface_ready(udev->handler, dev);
+
if (device_lookup_by_id(udev->handler, name)) {
pb_debug("device %s is already present?\n", name);
return -1;
@@ -324,10 +392,16 @@ static bool udev_handle_cdrom_events(struct pb_udev *udev,
static int udev_handle_dev_change(struct pb_udev *udev, struct udev_device *dev)
{
struct discover_device *ddev;
+ const char *subsys;
const char *name;
int rc = 0;
name = udev_device_get_sysname(dev);
+ subsys = udev_device_get_subsystem(dev);
+
+ /* If we see a net device, check if it is ready to be used */
+ if (!strncmp(subsys, "net", strlen("net")))
+ return udev_check_interface_ready(udev->handler, dev);
ddev = device_lookup_by_id(udev->handler, name);
@@ -392,6 +466,12 @@ static int udev_enumerate(struct udev *udev)
goto fail;
}
+ result = udev_enumerate_add_match_subsystem(enumerate, "net");
+ if (result) {
+ pb_log("udev_enumerate_add_match_subsystem failed\n");
+ goto fail;
+ }
+
result = udev_enumerate_add_match_is_initialized(enumerate);
if (result) {
pb_log("udev_enumerate_add_match_is_initialised failed\n");
@@ -449,6 +529,14 @@ static int udev_setup_monitor(struct udev *udev, struct udev_monitor **monitor)
goto out_err;
}
+ result = udev_monitor_filter_add_match_subsystem_devtype(m, "net",
+ NULL);
+
+ if (result) {
+ pb_log("udev_monitor_filter_add_match_subsystem_devtype failed\n");
+ goto out_err;
+ }
+
result = udev_monitor_enable_receiving(m);
if (result) {
OpenPOWER on IntegriCloud