From f4f6a4f1f106b173a4af3d35a51f8a1e5ff49369 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 7 Jan 2009 09:29:50 -0800 Subject: [PATCH] netdev: Implement netdev_monitor, for monitoring network device status changes. --- lib/netdev.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++- lib/netdev.h | 27 ++++++-- lib/svec.c | 27 +++++++- lib/svec.h | 3 + 4 files changed, 224 insertions(+), 9 deletions(-) diff --git a/lib/netdev.c b/lib/netdev.c index f9b27ace..8cf778f3 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -39,10 +39,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include "fatal-signal.h" #include "list.h" +#include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "packets.h" @@ -67,6 +69,13 @@ #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" @@ -963,6 +972,171 @@ netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *flagsp) return 0; } +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); +} + static void restore_all_flags(void *aux); /* Set up a signal hook to restore network device flags on program diff --git a/lib/netdev.h b/lib/netdev.h index 28d84321..ae77397a 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -31,18 +31,19 @@ * 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 +#include #include +/* 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; @@ -96,4 +97,18 @@ int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]); 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 */ diff --git a/lib/svec.c b/lib/svec.c index 7f7e7594..e2ac25d3 100644 --- a/lib/svec.c +++ b/lib/svec.c @@ -202,9 +202,18 @@ svec_diff(const struct svec *a, const struct svec *b, bool svec_contains(const struct svec *svec, const char *name) { + return svec_find(svec, name) != SIZE_MAX; +} + +size_t +svec_find(const struct svec *svec, const char *name) +{ + char **p; + assert(svec_is_sorted(svec)); - return bsearch(&name, svec->names, svec->n, sizeof *svec->names, - compare_strings) != NULL; + p = bsearch(&name, svec->names, svec->n, sizeof *svec->names, + compare_strings); + return p ? p - svec->names : SIZE_MAX; } bool @@ -337,3 +346,17 @@ svec_join(const struct svec *svec, const char *delimiter) } return ds_cstr(&ds); } + +const char * +svec_back(const struct svec *svec) +{ + assert(svec->n); + return svec->names[svec->n - 1]; +} + +void +svec_pop_back(struct svec *svec) +{ + assert(svec->n); + free(svec->names[--svec->n]); +} diff --git a/lib/svec.h b/lib/svec.h index 68d361c4..0cd21922 100644 --- a/lib/svec.h +++ b/lib/svec.h @@ -58,6 +58,7 @@ void svec_unique(struct svec *); void svec_diff(const struct svec *a, const struct svec *b, struct svec *a_only, struct svec *both, struct svec *b_only); bool svec_contains(const struct svec *, const char *); +size_t svec_find(const struct svec *, const char *); bool svec_is_sorted(const struct svec *); bool svec_is_unique(const struct svec *); const char *svec_get_duplicate(const struct svec *); @@ -66,5 +67,7 @@ void svec_print(const struct svec *svec, const char *title); void svec_parse_words(struct svec *svec, const char *words); bool svec_equal(const struct svec *, const struct svec *); char *svec_join(const struct svec *, const char *delimiter); +const char *svec_back(const struct svec *); +void svec_pop_back(struct svec *); #endif /* svec.h */ -- 2.30.2