From 21d6e22eeec05a1c382178dc2eb840afe3b9cca6 Mon Sep 17 00:00:00 2001 From: Ethan Jackson Date: Tue, 21 Dec 2010 13:44:37 -0800 Subject: [PATCH] rtnetlink: Remove LINK specific messages from rtnetlink Abstracted rtnetlink so that it may be used for messages other than RTM LINK messages. Created a new rtnetlink-link module which specifically deals with these kinds of messages and follows the old rtnetlink API. --- lib/automake.mk | 4 +- lib/dpif-linux.c | 15 +++-- lib/netdev-linux.c | 22 ++++--- lib/rtnetlink-link.c | 120 ++++++++++++++++++++++++++++++++++++ lib/rtnetlink-link.h | 53 ++++++++++++++++ lib/rtnetlink.c | 141 ++++++++++++++++++++----------------------- lib/rtnetlink.h | 48 +++++++-------- 7 files changed, 285 insertions(+), 118 deletions(-) create mode 100644 lib/rtnetlink-link.c create mode 100644 lib/rtnetlink-link.h diff --git a/lib/automake.mk b/lib/automake.mk index f7d14994..4c6a8772 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -189,7 +189,9 @@ lib_libopenvswitch_a_SOURCES += \ lib/netlink-socket.c \ lib/netlink-socket.h \ lib/rtnetlink.c \ - lib/rtnetlink.h + lib/rtnetlink.h \ + lib/rtnetlink-link.c \ + lib/rtnetlink-link.h endif if HAVE_OPENSSL diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index 66610cf2..1d7249c2 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -40,6 +40,7 @@ #include "openvswitch/tunnel.h" #include "poll-loop.h" #include "rtnetlink.h" +#include "rtnetlink-link.h" #include "shash.h" #include "svec.h" #include "util.h" @@ -72,7 +73,7 @@ static int get_openvswitch_major(void); static int create_minor(const char *name, int minor, struct dpif **dpifp); static int open_minor(int minor, struct dpif **dpifp); static int make_openvswitch_device(int minor, char **fnp); -static void dpif_linux_port_changed(const struct rtnetlink_change *, +static void dpif_linux_port_changed(const struct rtnetlink_link_change *, void *dpif); static struct dpif_linux * @@ -177,7 +178,7 @@ static void dpif_linux_close(struct dpif *dpif_) { struct dpif_linux *dpif = dpif_linux_cast(dpif_); - rtnetlink_notifier_unregister(&dpif->port_notifier); + rtnetlink_link_notifier_unregister(&dpif->port_notifier); shash_destroy(&dpif->changed_ports); free(dpif->local_ifname); close(dpif->fd); @@ -367,7 +368,7 @@ dpif_linux_port_poll_wait(const struct dpif *dpif_) if (!shash_is_empty(&dpif->changed_ports) || dpif->change_error) { poll_immediate_wake(); } else { - rtnetlink_notifier_wait(); + rtnetlink_link_notifier_wait(); } } @@ -770,8 +771,9 @@ open_minor(int minor, struct dpif **dpifp) fd = open(fn, O_RDONLY | O_NONBLOCK); if (fd >= 0) { struct dpif_linux *dpif = xmalloc(sizeof *dpif); - error = rtnetlink_notifier_register(&dpif->port_notifier, - dpif_linux_port_changed, dpif); + error = rtnetlink_link_notifier_register(&dpif->port_notifier, + dpif_linux_port_changed, + dpif); if (!error) { char *name; @@ -799,7 +801,8 @@ open_minor(int minor, struct dpif **dpifp) } static void -dpif_linux_port_changed(const struct rtnetlink_change *change, void *dpif_) +dpif_linux_port_changed(const struct rtnetlink_link_change *change, + void *dpif_) { struct dpif_linux *dpif = dpif_; diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 1efbfd88..7f9a8e32 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -59,6 +59,7 @@ #include "packets.h" #include "poll-loop.h" #include "rtnetlink.h" +#include "rtnetlink-link.h" #include "socket-util.h" #include "shash.h" #include "svec.h" @@ -457,17 +458,17 @@ netdev_linux_init(void) static void netdev_linux_run(void) { - rtnetlink_notifier_run(); + rtnetlink_link_notifier_run(); } static void netdev_linux_wait(void) { - rtnetlink_notifier_wait(); + rtnetlink_link_notifier_wait(); } static void -netdev_linux_cache_cb(const struct rtnetlink_change *change, +netdev_linux_cache_cb(const struct rtnetlink_link_change *change, void *aux OVS_UNUSED) { struct netdev_dev_linux *dev; @@ -511,8 +512,8 @@ netdev_linux_create(const struct netdev_class *class, } if (!cache_notifier_refcount) { - error = rtnetlink_notifier_register(&netdev_linux_cache_notifier, - netdev_linux_cache_cb, NULL); + error = rtnetlink_link_notifier_register(&netdev_linux_cache_notifier, + netdev_linux_cache_cb, NULL); if (error) { return error; } @@ -608,7 +609,7 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_) cache_notifier_refcount--; if (!cache_notifier_refcount) { - rtnetlink_notifier_unregister(&netdev_linux_cache_notifier); + rtnetlink_link_notifier_unregister(&netdev_linux_cache_notifier); } } else if (class == &netdev_tap_class) { destroy_tap(netdev_dev); @@ -2049,7 +2050,7 @@ poll_notify(struct list *list) } static void -netdev_linux_poll_cb(const struct rtnetlink_change *change, +netdev_linux_poll_cb(const struct rtnetlink_link_change *change, void *aux OVS_UNUSED) { if (change) { @@ -2076,8 +2077,9 @@ netdev_linux_poll_add(struct netdev *netdev, struct list *list; if (shash_is_empty(&netdev_linux_notifiers)) { - int error = rtnetlink_notifier_register(&netdev_linux_poll_notifier, - netdev_linux_poll_cb, NULL); + int error; + error = rtnetlink_link_notifier_register(&netdev_linux_poll_notifier, + netdev_linux_poll_cb, NULL); if (error) { return error; } @@ -2117,7 +2119,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) /* If that was the last notifier, unregister. */ if (shash_is_empty(&netdev_linux_notifiers)) { - rtnetlink_notifier_unregister(&netdev_linux_poll_notifier); + rtnetlink_link_notifier_unregister(&netdev_linux_poll_notifier); } } diff --git a/lib/rtnetlink-link.c b/lib/rtnetlink-link.c new file mode 100644 index 00000000..ffd615bf --- /dev/null +++ b/lib/rtnetlink-link.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2009, 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "rtnetlink-link.h" + +#include +#include +#include + +#include "netlink.h" +#include "ofpbuf.h" +#include "rtnetlink.h" + +static struct rtnetlink *rtn = NULL; +static struct rtnetlink_link_change rtn_change; + +static bool +rtnetlink_link_parse(struct ofpbuf *buf, + struct rtnetlink_link_change *change) +{ + bool parsed; + + /* Policy for RTNLGRP_LINK messages. + * + * There are *many* more fields in these messages, but currently we + * only care about these fields. */ + static const struct nl_policy policy[] = { + [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, + [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, + }; + + static struct nlattr *attrs[ARRAY_SIZE(policy)]; + + parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), + policy, attrs, ARRAY_SIZE(policy)); + + if (parsed) { + const struct nlmsghdr *nlmsg; + const struct ifinfomsg *ifinfo; + + nlmsg = buf->data; + ifinfo = ((const struct ifinfomsg *) + ((const char *) buf->data + NLMSG_HDRLEN)); + + change->nlmsg_type = nlmsg->nlmsg_type; + change->ifi_index = ifinfo->ifi_index; + change->ifname = nl_attr_get_string(attrs[IFLA_IFNAME]); + change->master_ifindex = (attrs[IFLA_MASTER] + ? nl_attr_get_u32(attrs[IFLA_MASTER]) + : 0); + } + + return parsed; +} + +/* Registers 'cb' to be called with auxiliary data 'aux' with network device + * change notifications. The notifier is stored in 'notifier', which the + * caller must not modify or free. + * + * This is probably not the function that you want. You should probably be + * using dpif_port_poll() or netdev_monitor_create(), which unlike this + * function are not Linux-specific. + * + * Returns 0 if successful, otherwise a positive errno value. */ +int +rtnetlink_link_notifier_register(struct rtnetlink_notifier *notifier, + rtnetlink_link_notify_func *cb, void *aux) +{ + rtnetlink_parse_func *pf = (rtnetlink_parse_func *) rtnetlink_link_parse; + rtnetlink_notify_func *nf = (rtnetlink_notify_func *) cb; + + if (!rtn) { + rtn = rtnetlink_create(RTNLGRP_LINK, pf, &rtn_change); + } + + return rtnetlink_notifier_register(rtn, notifier, nf, aux); +} + +/* Cancels notification on 'notifier', which must have previously been + * registered with rtnetlink_link_notifier_register(). */ +void +rtnetlink_link_notifier_unregister(struct rtnetlink_notifier *notifier) +{ + rtnetlink_notifier_unregister(rtn, notifier); +} + +/* Calls all of the registered notifiers, passing along any as-yet-unreported + * netdev change events. */ +void +rtnetlink_link_notifier_run(void) +{ + if (rtn) { + rtnetlink_notifier_run(rtn); + } +} + +/* Causes poll_block() to wake up when network device change notifications are + * ready. */ +void +rtnetlink_link_notifier_wait(void) +{ + if (rtn) { + rtnetlink_notifier_wait(rtn); + } +} diff --git a/lib/rtnetlink-link.h b/lib/rtnetlink-link.h new file mode 100644 index 00000000..9248b0ad --- /dev/null +++ b/lib/rtnetlink-link.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2009 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RTNETLINK_LINK_H +#define RTNETLINK_LINK_H 1 + +struct rtnetlink_notifier; + +/* These functions are Linux specific, so they should be used directly only by + * Linux-specific code. */ + +/* A digested version of an rtnetlink_link message sent down by the kernel to + * indicate that a network device has been created, destroyed or changed. */ +struct rtnetlink_link_change { + /* Copied from struct nlmsghdr. */ + int nlmsg_type; /* e.g. RTM_NEWLINK, RTM_DELLINK. */ + + /* Copied from struct ifinfomsg. */ + int ifi_index; /* Index of network device. */ + + /* Extracted from Netlink attributes. */ + const char *ifname; /* Name of network device. */ + int master_ifindex; /* Ifindex of datapath master (0 if none). */ +}; + +/* Function called to report that a netdev has changed. 'change' describes the + * specific change. It may be null if the buffer of change information + * overflowed, in which case the function must assume that every device may + * have changed. 'aux' is as specified in the call to + * rtnetlink_link_notifier_register(). */ +typedef +void rtnetlink_link_notify_func(const struct rtnetlink_link_change *change, + void *aux); + +int rtnetlink_link_notifier_register(struct rtnetlink_notifier *, + rtnetlink_link_notify_func *, void *aux); +void rtnetlink_link_notifier_unregister(struct rtnetlink_notifier *); +void rtnetlink_link_notifier_run(void); +void rtnetlink_link_notifier_wait(void); +#endif /* rtnetlink-link.h */ diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c index 54340e5c..5d80d725 100644 --- a/lib/rtnetlink.c +++ b/lib/rtnetlink.c @@ -19,9 +19,6 @@ #include "rtnetlink.h" #include -#include -#include -#include #include #include "coverage.h" @@ -34,33 +31,54 @@ VLOG_DEFINE_THIS_MODULE(rtnetlink); COVERAGE_DEFINE(rtnetlink_changed); -/* rtnetlink socket. */ -static struct nl_sock *notify_sock; +static void rtnetlink_report(struct rtnetlink *rtn, void *change); + +struct rtnetlink { + struct nl_sock *notify_sock; /* Rtnetlink socket. */ + struct list all_notifiers; /* All rtnetlink notifiers. */ + + /* Passed in by rtnetlink_create(). */ + int multicast_group; /* Multicast group we listen on. */ + rtnetlink_parse_func *parse; /* Message parsing function. */ + void *change; /* Change passed to parse. */ +}; + +/* Creates an rtnetlink handle which may be used to manage change + * notifications. The created handle will listen for rtnetlink messages on + * 'multicast_group'. Incoming messages will be parsed with 'parse' which will + * be passed 'change' as an argument. */ +struct rtnetlink * +rtnetlink_create(int multicast_group, rtnetlink_parse_func *parse, + void *change) +{ + struct rtnetlink *rtn; -/* All registered notifiers. */ -static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers); + rtn = xzalloc(sizeof *rtn); + rtn->notify_sock = 0; + rtn->multicast_group = multicast_group; + rtn->parse = parse; + rtn->change = change; -static void rtnetlink_report_change(const struct nlmsghdr *, - const struct ifinfomsg *, - struct nlattr *attrs[]); -static void rtnetlink_report_notify_error(void); + list_init(&rtn->all_notifiers); + return rtn; +} -/* Registers 'cb' to be called with auxiliary data 'aux' with network device - * change notifications. The notifier is stored in 'notifier', which the - * caller must not modify or free. +/* Registers 'cb' to be called with auxiliary data 'aux' with change + * notifications. The notifier is stored in 'notifier', which the caller must + * not modify or free. * - * This is probably not the function that you want. You should probably be - * using dpif_port_poll() or netdev_monitor_create(), which unlike this - * function are not Linux-specific. + * This is probably not the function you want. You should probably be using + * message specific notifiers like rtnetlink_link_notifier_register(). * * Returns 0 if successful, otherwise a positive errno value. */ int -rtnetlink_notifier_register(struct rtnetlink_notifier *notifier, +rtnetlink_notifier_register(struct rtnetlink *rtn, + struct rtnetlink_notifier *notifier, rtnetlink_notify_func *cb, void *aux) { - if (!notify_sock) { - int error = nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, - ¬ify_sock); + if (!rtn->notify_sock) { + int error = nl_sock_create(NETLINK_ROUTE, rtn->multicast_group, 0, 0, + &rtn->notify_sock); if (error) { VLOG_WARN("could not create rtnetlink socket: %s", strerror(error)); @@ -69,10 +87,10 @@ rtnetlink_notifier_register(struct rtnetlink_notifier *notifier, } else { /* Catch up on notification work so that the new notifier won't * receive any stale notifications. */ - rtnetlink_notifier_run(); + rtnetlink_notifier_run(rtn); } - list_push_back(&all_notifiers, ¬ifier->node); + list_push_back(&rtn->all_notifiers, ¬ifier->node); notifier->cb = cb; notifier->aux = aux; return 0; @@ -81,52 +99,38 @@ rtnetlink_notifier_register(struct rtnetlink_notifier *notifier, /* Cancels notification on 'notifier', which must have previously been * registered with rtnetlink_notifier_register(). */ void -rtnetlink_notifier_unregister(struct rtnetlink_notifier *notifier) +rtnetlink_notifier_unregister(struct rtnetlink *rtn, + struct rtnetlink_notifier *notifier) { list_remove(¬ifier->node); - if (list_is_empty(&all_notifiers)) { - nl_sock_destroy(notify_sock); - notify_sock = NULL; + if (list_is_empty(&rtn->all_notifiers)) { + nl_sock_destroy(rtn->notify_sock); + rtn->notify_sock = NULL; } } /* Calls all of the registered notifiers, passing along any as-yet-unreported - * netdev change events. */ + * change events. */ void -rtnetlink_notifier_run(void) +rtnetlink_notifier_run(struct rtnetlink *rtn) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - if (!notify_sock) { + if (!rtn->notify_sock) { return; } for (;;) { - /* Policy for RTNLGRP_LINK messages. - * - * There are *many* more fields in these messages, but currently we - * only care about these fields. */ - static const struct nl_policy rtnetlink_policy[] = { - [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, - [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, - }; - - struct nlattr *attrs[ARRAY_SIZE(rtnetlink_policy)]; struct ofpbuf *buf; int error; - error = nl_sock_recv(notify_sock, &buf, false); + error = nl_sock_recv(rtn->notify_sock, &buf, false); if (!error) { - if (nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), - rtnetlink_policy, - attrs, ARRAY_SIZE(rtnetlink_policy))) { - struct ifinfomsg *ifinfo; - - ifinfo = (void *) ((char *) buf->data + NLMSG_HDRLEN); - rtnetlink_report_change(buf->data, ifinfo, attrs); + if (rtn->parse(buf, rtn->change)) { + rtnetlink_report(rtn, rtn->change); } else { VLOG_WARN_RL(&rl, "received bad rtnl message"); - rtnetlink_report_notify_error(); + rtnetlink_report(rtn, NULL); } ofpbuf_delete(buf); } else if (error == EAGAIN) { @@ -138,48 +142,31 @@ rtnetlink_notifier_run(void) VLOG_WARN_RL(&rl, "error reading rtnetlink socket: %s", strerror(error)); } - rtnetlink_report_notify_error(); + rtnetlink_report(rtn, NULL); } } } -/* Causes poll_block() to wake up when network device change notifications are - * ready. */ +/* Causes poll_block() to wake up when change notifications are ready. */ void -rtnetlink_notifier_wait(void) +rtnetlink_notifier_wait(struct rtnetlink *rtn) { - if (notify_sock) { - nl_sock_wait(notify_sock, POLLIN); + if (rtn->notify_sock) { + nl_sock_wait(rtn->notify_sock, POLLIN); } } static void -rtnetlink_report_change(const struct nlmsghdr *nlmsg, - const struct ifinfomsg *ifinfo, - struct nlattr *attrs[]) +rtnetlink_report(struct rtnetlink *rtn, void *change) { struct rtnetlink_notifier *notifier; - struct rtnetlink_change change; - COVERAGE_INC(rtnetlink_changed); - - change.nlmsg_type = nlmsg->nlmsg_type; - change.ifi_index = ifinfo->ifi_index; - change.ifname = nl_attr_get_string(attrs[IFLA_IFNAME]); - change.master_ifindex = (attrs[IFLA_MASTER] - ? nl_attr_get_u32(attrs[IFLA_MASTER]) : 0); - - LIST_FOR_EACH (notifier, node, &all_notifiers) { - notifier->cb(&change, notifier->aux); + if (change) { + COVERAGE_INC(rtnetlink_changed); } -} -static void -rtnetlink_report_notify_error(void) -{ - struct rtnetlink_notifier *notifier; - - LIST_FOR_EACH (notifier, node, &all_notifiers) { - notifier->cb(NULL, notifier->aux); + LIST_FOR_EACH (notifier, node, &rtn->all_notifiers) { + notifier->cb(change, notifier->aux); } } + diff --git a/lib/rtnetlink.h b/lib/rtnetlink.h index 8f188055..3d4b51e7 100644 --- a/lib/rtnetlink.h +++ b/lib/rtnetlink.h @@ -22,26 +22,22 @@ #include "list.h" -/* A digested version of an rtnetlink message sent down by the kernel to - * indicate that a network device has been created or destroyed or changed. */ -struct rtnetlink_change { - /* Copied from struct nlmsghdr. */ - int nlmsg_type; /* e.g. RTM_NEWLINK, RTM_DELLINK. */ - - /* Copied from struct ifinfomsg. */ - int ifi_index; /* Index of network device. */ - - /* Extracted from Netlink attributes. */ - const char *ifname; /* Name of network device. */ - int master_ifindex; /* Ifindex of datapath master (0 if none). */ -}; +struct rtnetlink; +struct nlattr; +struct ofpbuf; + +/* Function called to report rtnetlink notifications. 'change' describes the + * specific change filled out by an rtnetlink_parse_func. It may be null if + * the buffer of change information overflowed, in which case the function must + * assume that everything may have changed. 'aux' is as specified in + * rtnetlink_notifier_register(). + */ +typedef void rtnetlink_notify_func(const void *change, void *aux); -/* Function called to report that a netdev has changed. 'change' describes the - * specific change. It may be null if the buffer of change information - * overflowed, in which case the function must assume that every device may - * have changed. 'aux' is as specified in the call to - * rtnetlink_notifier_register(). */ -typedef void rtnetlink_notify_func(const struct rtnetlink_change *, void *aux); +/* Function called to parse incoming rtnetlink notifications. The 'buf' + * message should be parsed into 'change' as specified in rtnetlink_create(). + */ +typedef bool rtnetlink_parse_func(struct ofpbuf *buf, void *change); struct rtnetlink_notifier { struct list node; @@ -49,10 +45,14 @@ struct rtnetlink_notifier { void *aux; }; -int rtnetlink_notifier_register(struct rtnetlink_notifier *, +struct rtnetlink *rtnetlink_create(int multicast_group, + rtnetlink_parse_func *, + void *change); +int rtnetlink_notifier_register(struct rtnetlink *, + struct rtnetlink_notifier *, rtnetlink_notify_func *, void *aux); -void rtnetlink_notifier_unregister(struct rtnetlink_notifier *); -void rtnetlink_notifier_run(void); -void rtnetlink_notifier_wait(void); - +void rtnetlink_notifier_unregister(struct rtnetlink *, + struct rtnetlink_notifier *); +void rtnetlink_notifier_run(struct rtnetlink *); +void rtnetlink_notifier_wait(struct rtnetlink *); #endif /* rtnetlink.h */ -- 2.30.2