The purpose of this function is to fetch link state
and link speed for given NIC name from the SYSFS.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/util/virnetdev.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++
src/util/virnetdev.h | 6 +++
3 files changed, 111 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 394c086..6d08ee9 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1540,6 +1540,7 @@ virNetDevClearIPv4Address;
virNetDevExists;
virNetDevGetIndex;
virNetDevGetIPv4Address;
+virNetDevGetLinkInfo;
virNetDevGetMAC;
virNetDevGetMTU;
virNetDevGetPhysicalFunction;
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 3a60cf7..9d2c0d2 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -1832,3 +1832,107 @@ virNetDevRestoreNetConfig(const char *linkdev ATTRIBUTE_UNUSED,
}
#endif /* defined(__linux__) && defined(HAVE_LIBNL) */
+
+#ifdef __linux__
+# define SYSFS_PREFIX "/sys/class/net/"
+int
+virNetDevGetLinkInfo(const char *ifname,
+ virInterfaceState *state,
+ unsigned int *speed)
+{
+ int ret = -1;
+ char *path = NULL;
+ char *buf = NULL;
+ char *tmp;
+ int tmp_state;
+ unsigned int tmp_speed;
+
+ if (state) {
+ if (virAsprintf(&path, SYSFS_PREFIX "%s/operstate", ifname) <
0)
+ goto cleanup;
+
+ if (virFileReadAll(path, 1024, &buf) < 0) {
+ virReportSystemError(errno,
+ _("unable to read: %s"),
+ path);
+ goto cleanup;
+ }
+
+ if (!(tmp = strchr(buf, '\n'))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse: %s"),
+ buf);
+ goto cleanup;
+ }
+
+ *tmp = '\0';
+
+ /* We shouldn't allow 0 here, because
+ * virInterfaceState enum starts from 1. */
+ if ((tmp_state = virInterfaceStateTypeFromString(buf)) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse: %s"),
+ buf);
+ goto cleanup;
+ }
+
+ *state = tmp_state;
+
+ VIR_FREE(path);
+ VIR_FREE(buf);
+ }
+
+ if (speed) {
+ if (virAsprintf(&path, SYSFS_PREFIX "%s/speed", ifname) < 0)
+ goto cleanup;
+
+ if (virFileReadAll(path, 1024, &buf) < 0) {
+ /* Some devices doesn't report speed, in which case we get EINVAL */
+ if (errno == EINVAL) {
+ ret = 0;
+ goto cleanup;
+ }
+ virReportSystemError(errno,
+ _("unable to read: %s"),
+ path);
+ goto cleanup;
+ }
+
+ if (virStrToLong_ui(buf, &tmp, 10, &tmp_speed) < 0 ||
+ *tmp != '\n') {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse: %s"),
+ buf);
+ goto cleanup;
+ }
+
+ /* Workaround broken kernel API. If the link is unplugged then
+ * depending on the NIC driver, link speed can be reported as -1.
+ * However, the value is printed out as unsigned integer instead of
+ * signed one. Terrifying but true. */
+ *speed = (int) tmp_speed == -1 ? 0 : tmp_speed;
+ }
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(buf);
+ VIR_FREE(path);
+ return ret;
+}
+
+#else
+
+int
+virNetDevGetLinkInfo(const char *ifname,
+ virInterfaceState *state,
+ unsigned int *speed)
+{
+ /* Port me */
+ VIR_DEBUG("Getting link info on %s is not implemented on this platform");
+ if (state)
+ *state = 0;
+ if (speed)
+ *speed = 0;
+ return 0;
+}
+#endif /* defined(__linux__) */
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 00c82e0..de33323 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -29,6 +29,7 @@
# include "virnetlink.h"
# include "virmacaddr.h"
# include "virpci.h"
+# include "device_conf.h"
# ifdef HAVE_STRUCT_IFREQ
typedef struct ifreq virIfreq;
@@ -145,4 +146,9 @@ int virNetDevGetVirtualFunctionInfo(const char *vfname, char
**pfname,
int *vf)
ATTRIBUTE_NONNULL(1);
+int virNetDevGetLinkInfo(const char *ifname,
+ virInterfaceState *state,
+ unsigned int *speed)
+ ATTRIBUTE_NONNULL(1);
+
#endif /* __VIR_NETDEV_H__ */
--
2.0.0