Bug #63055 mysql assumes all ethernet devices are named ethX
Submitted: 1 Nov 2011 11:45 Modified: 13 Nov 2013 23:36
Reporter: Honza Horak (OCA) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: DML Severity:S3 (Non-critical)
Version:5.5.17, 5.5.20 OS:Linux
Assigned to: CPU Architecture:Any
Tags: Contribution
Triage: Needs Triage: D3 (Medium)

[1 Nov 2011 11:45] Honza Horak
Description:
This bug was originally reported in [1].

We can't rely on the fact that network device names are ethX any more. If we use biosdevname Udev helper [2], then for example Ethernet devices are labeled em1, but any other name can be used generally.

MySQL reads the HW address of any non-zero ethX device to generate an UUID. When a user uses biosdevname (which is default in Fedora by the way) then my_gethwaddr() returns 00:00:00:00:00:00 now. 

We can loop through all devices using SIOCGIFCONF and get their HW address. There is a comment in mysys/my_gethwaddr.c that says we can use any of non-zero devices, which is how the suggested fix works.

[1] https://bugzilla.redhat.com/show_bug.cgi?id=682365
[2] http://freecode.com/projects/biosdevname

How to repeat:
Log values returned by my_gethwaddr() when biosdevname is used.

Suggested fix:
diff -up mysql-5.5.15/mysys/my_gethwaddr.c.netdevname mysql-5.5.15/mysys/my_gethwaddr.c
--- mysql-5.5.15/mysys/my_gethwaddr.c.netdevname	2011-07-13 21:09:02.000000000 +0200
+++ mysql-5.5.15/mysys/my_gethwaddr.c	2011-11-01 12:32:35.356119715 +0100
@@ -68,28 +68,47 @@ err:
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 
+#define MAX_IFS 64
+
 my_bool my_gethwaddr(uchar *to)
 {
   int fd, res= 1;
   struct ifreq ifr;
   char zero_array[ETHER_ADDR_LEN] = {0};
+  struct ifconf ifc;
+  struct ifreq ifs[MAX_IFS], *ifri, *ifend;
 
   fd = socket(AF_INET, SOCK_DGRAM, 0);
   if (fd < 0)
     goto err;
 
-  bzero(&ifr, sizeof(ifr));
-  strnmov(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1);
+  ifc.ifc_len = sizeof(ifs);
+  ifc.ifc_req = ifs;
+  if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
+  {
+    close(fd);
+    goto err;
+  }
+
+  memcpy(to, zero_array, ETHER_ADDR_LEN);
 
-  do
+  ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
+  for (ifri = ifc.ifc_req; ifri < ifend; ifri++)
   {
-    if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0)
+    if (ifri->ifr_addr.sa_family == AF_INET)
     {
-      memcpy(to, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
-      res= memcmp(to, zero_array, ETHER_ADDR_LEN) ? 0 : 1;
+      bzero(&ifr, sizeof(ifr));
+      strncpy(ifr.ifr_name, ifri->ifr_name, sizeof(ifr.ifr_name));
+      
+      /* Get HW address */
+      if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0)
+      {
+        memcpy(to, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+        if (!(res= memcmp(to, zero_array, ETHER_ADDR_LEN) ? 0 : 1))
+          break;
+      }
     }
-  } while (res && (errno == 0 || errno == ENODEV) && ifr.ifr_name[3]++ < '6');
-
+  }
   close(fd);
 err:
   return res;
[20 Dec 2011 14:22] Valeriy Kravchuk
Thank you for the patch contributed.
[13 Nov 2013 23:36] Paul Dubois
Noted in 5.6.15, 5.7.3 changelogs.

The server uses the ethernet hardware address for UUID generation,
but made assumptions about the names of ethernet devices rather than
querying the system for their names. Thanks to Honza Horak for the
patch.