X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Frtnetlink.c;h=6ed85abbba2b5e51f35ca969e75f041f6c9ddb1b;hb=6d9e6eb44fc82ae985e6e8db4f156ba56f8bea39;hp=54340e5c034cc47cd841b9c45491d2aac6eb9613;hpb=2fe27d5ad27f3c7879ea696209bcf9702d9b7109;p=sliver-openvswitch.git diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c index 54340e5c0..6ed85abbb 100644 --- a/lib/rtnetlink.c +++ b/lib/rtnetlink.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010 Nicira Networks. + * Copyright (c) 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,8 @@ #include "rtnetlink.h" #include -#include -#include -#include #include +#include #include "coverage.h" #include "netlink.h" @@ -34,45 +32,84 @@ 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; + + rtn = xzalloc(sizeof *rtn); + rtn->notify_sock = 0; + rtn->multicast_group = multicast_group; + rtn->parse = parse; + rtn->change = change; -/* All registered notifiers. */ -static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers); + list_init(&rtn->all_notifiers); + return rtn; +} -static void rtnetlink_report_change(const struct nlmsghdr *, - const struct ifinfomsg *, - struct nlattr *attrs[]); -static void rtnetlink_report_notify_error(void); +/* Destroys 'rtn' by freeing any memory it has reserved and closing any sockets + * it has opened. */ +void +rtnetlink_destroy(struct rtnetlink *rtn) +{ + if (rtn) { + nl_sock_destroy(rtn->notify_sock); + free(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) { + struct nl_sock *sock; + int error; + + error = nl_sock_create(NETLINK_ROUTE, &sock); + if (!error) { + error = nl_sock_join_mcgroup(sock, rtn->multicast_group); + } if (error) { + nl_sock_destroy(sock); VLOG_WARN("could not create rtnetlink socket: %s", strerror(error)); return error; } + rtn->notify_sock = sock; } 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 +118,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 +161,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); } } +