#include <fcntl.h>
#include <arpa/inet.h>
#include <inttypes.h>
-#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/types.h>
#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <linux/version.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
+#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_packet.h>
#include <net/route.h>
#include "fatal-signal.h"
#include "list.h"
+#include "netlink.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
#include "socket-util.h"
#include "svec.h"
+/* linux/if.h defines IFF_LOWER_UP, net/if.h doesn't.
+ * net/if.h defines if_nameindex(), linux/if.h doesn't.
+ * We can't include both headers, so define IFF_LOWER_UP ourselves. */
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000
+#endif
+
#define THIS_MODULE VLM_netdev
#include "vlog.h"
return 0;
}
\f
+struct netdev_monitor {
+ struct nl_sock *sock;
+ struct svec netdevs;
+ struct svec changed;
+};
+
+/* Policy for RTNLGRP_LINK messages.
+ *
+ * There are *many* more fields in these messages, but currently we only care
+ * about interface names. */
+static const struct nl_policy rtnlgrp_link_policy[] = {
+ [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
+};
+
+static const char *lookup_netdev(const struct netdev_monitor *, const char *);
+static const char *pop_changed(struct netdev_monitor *);
+static const char *all_netdevs_changed(struct netdev_monitor *);
+
+/* Creates a new network device monitor that initially monitors no
+ * devices. On success, sets '*monp' to the new network monitor and returns
+ * 0; on failure, sets '*monp' to a null pointer and returns a positive errno
+ * value. */
+int
+netdev_monitor_create(struct netdev_monitor **monp)
+{
+ struct netdev_monitor *mon;
+ struct nl_sock *sock;
+ int error;
+
+ *monp = NULL;
+ error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &sock);
+ if (error) {
+ /* XXX Fall back to polling? Non-root is not allowed to subscribe to
+ * multicast groups but can still poll network device state. */
+ VLOG_WARN("could not create rtnetlink socket: %s", strerror(error));
+ return error;
+ }
+
+ mon = *monp = xmalloc(sizeof *mon);
+ mon->sock = sock;
+ svec_init(&mon->netdevs);
+ svec_init(&mon->changed);
+ return 0;
+}
+
+void
+netdev_monitor_destroy(struct netdev_monitor *mon)
+{
+ if (mon) {
+ nl_sock_destroy(mon->sock);
+ svec_destroy(&mon->netdevs);
+ svec_destroy(&mon->changed);
+ free(mon);
+ }
+}
+
+/* Sets the set of network devices monitored by 'mon' to the 'n_netdevs'
+ * network devices named in 'netdevs'. The caller retains ownership of
+ * 'netdevs'. */
+void
+netdev_monitor_set_devices(struct netdev_monitor *mon,
+ char **netdevs, size_t n_netdevs)
+{
+ size_t i;
+
+ svec_clear(&mon->netdevs);
+ for (i = 0; i < n_netdevs; i++) {
+ svec_add(&mon->netdevs, netdevs[i]);
+ }
+ svec_sort(&mon->netdevs);
+}
+
+/* If the state of any network device has changed, returns its name. The
+ * caller must not modify or free the name.
+ *
+ * This function can return "false positives". The caller is responsible for
+ * verifying that the network device's state actually changed, if necessary.
+ *
+ * If no network device's state has changed, returns a null pointer. */
+const char *
+netdev_monitor_poll(struct netdev_monitor *mon)
+{
+ static struct vlog_rate_limit slow_rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const char *changed_name;
+
+ changed_name = pop_changed(mon);
+ if (changed_name) {
+ return changed_name;
+ }
+
+ for (;;) {
+ struct ofpbuf *buf;
+ int retval;
+
+ retval = nl_sock_recv(mon->sock, &buf, false);
+ if (retval == EAGAIN) {
+ return NULL;
+ } else if (retval == ENOBUFS) {
+ VLOG_WARN_RL(&slow_rl, "network monitor socket overflowed");
+ return all_netdevs_changed(mon);
+ } else if (retval) {
+ VLOG_WARN_RL(&slow_rl, "error on network monitor socket: %s",
+ strerror(retval));
+ return NULL;
+ } else {
+ struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
+ const char *name;
+
+ if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+ rtnlgrp_link_policy,
+ attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
+ VLOG_WARN_RL(&slow_rl, "received bad rtnl message");
+ return all_netdevs_changed(mon);
+ }
+ name = lookup_netdev(mon, nl_attr_get_string(attrs[IFLA_IFNAME]));
+ ofpbuf_delete(buf);
+ if (name) {
+ /* Return the looked-up string instead of the attribute string,
+ * because we freed the buffer that contains the attribute. */
+ return name;
+ }
+ }
+ }
+}
+
+void
+netdev_monitor_run(struct netdev_monitor *mon UNUSED)
+{
+ /* Nothing to do in this implementation. */
+}
+
+void
+netdev_monitor_wait(struct netdev_monitor *mon)
+{
+ nl_sock_wait(mon->sock, POLLIN);
+}
+
+static const char *
+lookup_netdev(const struct netdev_monitor *mon, const char *name)
+{
+ size_t idx = svec_find(&mon->netdevs, name);
+ return idx ? mon->netdevs.names[idx] : NULL;
+}
+
+static const char *
+pop_changed(struct netdev_monitor *mon)
+{
+ while (mon->changed.n) {
+ const char *name = lookup_netdev(mon, svec_back(&mon->changed));
+ svec_pop_back(&mon->changed);
+ if (name) {
+ return name;
+ }
+ }
+ return NULL;
+}
+
+static const char *
+all_netdevs_changed(struct netdev_monitor *mon)
+{
+ svec_clear(&mon->changed);
+ svec_append(&mon->changed, &mon->netdevs);
+ return pop_changed(mon);
+}
+\f
static void restore_all_flags(void *aux);
/* Set up a signal hook to restore network device flags on program
* derivatives without specific, written prior permission.
*/
-/* Generic interface to network devices.
- *
- * Currently, there is a single implementation of this interface that supports
- * Linux. The interface should be generic enough to be implementable on other
- * operating systems as well. */
-
#ifndef NETDEV_H
#define NETDEV_H 1
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
+/* Generic interface to network devices.
+ *
+ * Currently, there is a single implementation of this interface that supports
+ * Linux. The interface should be generic enough to be implementable on other
+ * operating systems as well. */
+
struct ofpbuf;
struct in_addr;
struct in6_addr;
void netdev_enumerate(struct svec *);
int netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *);
+/* Generic interface for monitoring network devices.
+ *
+ * A network device monitor keeps track of the state of network devices and
+ * reports when that state changes. At a minimum, it must report when a
+ * device's link status changes.
+ */
+struct netdev_monitor;
+int netdev_monitor_create(struct netdev_monitor **);
+void netdev_monitor_destroy(struct netdev_monitor *);
+void netdev_monitor_set_devices(struct netdev_monitor *, char **, size_t);
+const char *netdev_monitor_poll(struct netdev_monitor *);
+void netdev_monitor_run(struct netdev_monitor *);
+void netdev_monitor_wait(struct netdev_monitor *);
+
#endif /* netdev.h */