LCOV - code coverage report
Current view: top level - lib - netlink-notifier.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 67 84 79.8 %
Date: 2016-09-14 01:02:56 Functions: 12 13 92.3 %
Branches: 26 52 50.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <config.h>
      18                 :            : 
      19                 :            : #include "netlink-notifier.h"
      20                 :            : 
      21                 :            : #include <errno.h>
      22                 :            : #include <poll.h>
      23                 :            : #include <stdlib.h>
      24                 :            : 
      25                 :            : #include "coverage.h"
      26                 :            : #include "netlink.h"
      27                 :            : #include "netlink-socket.h"
      28                 :            : #include "openvswitch/ofpbuf.h"
      29                 :            : #include "openvswitch/vlog.h"
      30                 :            : 
      31                 :      20190 : VLOG_DEFINE_THIS_MODULE(netlink_notifier);
      32                 :            : 
      33                 :     110706 : COVERAGE_DEFINE(nln_changed);
      34                 :            : 
      35                 :            : static void nln_report(const struct nln *nln, void *change, int group);
      36                 :            : 
      37                 :            : struct nln {
      38                 :            :     struct nl_sock *notify_sock; /* Netlink socket. */
      39                 :            :     struct ovs_list all_notifiers;   /* All nln notifiers. */
      40                 :            :     bool has_run;                /* Guard for run and wait functions. */
      41                 :            : 
      42                 :            :     /* Passed in by nln_create(). */
      43                 :            :     int protocol;                /* Protocol passed to nl_sock_create(). */
      44                 :            :     nln_parse_func *parse;       /* Message parsing function. */
      45                 :            :     void *change;                /* Change passed to parse. */
      46                 :            : };
      47                 :            : 
      48                 :            : struct nln_notifier {
      49                 :            :     struct ovs_list node;        /* In struct nln's 'all_notifiers' list. */
      50                 :            :     struct nln *nln;             /* Parent nln. */
      51                 :            : 
      52                 :            :     int multicast_group;         /* Multicast group we listen on. */
      53                 :            :     nln_notify_func *cb;
      54                 :            :     void *aux;
      55                 :            : };
      56                 :            : 
      57                 :            : /* Creates an nln handle which may be used to manage change notifications.  The
      58                 :            :  * created handle will listen for netlink messages on 'multicast_group' using
      59                 :            :  * netlink protocol 'protocol' (e.g. NETLINK_ROUTE, NETLINK_GENERIC, ...).
      60                 :            :  * Incoming messages will be parsed with 'parse' which will be passed 'change'
      61                 :            :  * as an argument. */
      62                 :            : struct nln *
      63                 :       1370 : nln_create(int protocol, nln_parse_func *parse, void *change)
      64                 :            : {
      65                 :            :     struct nln *nln;
      66                 :            : 
      67                 :       1370 :     nln = xzalloc(sizeof *nln);
      68                 :       1370 :     nln->notify_sock = NULL;
      69                 :       1370 :     nln->protocol = protocol;
      70                 :       1370 :     nln->parse = parse;
      71                 :       1370 :     nln->change = change;
      72                 :       1370 :     nln->has_run = false;
      73                 :            : 
      74                 :       1370 :     ovs_list_init(&nln->all_notifiers);
      75                 :       1370 :     return nln;
      76                 :            : }
      77                 :            : 
      78                 :            : /* Destroys 'nln' by freeing any memory it has reserved and closing any sockets
      79                 :            :  * it has opened.
      80                 :            :  *
      81                 :            :  * The caller is responsible for destroying any notifiers created by this
      82                 :            :  * 'nln' before destroying 'nln'. */
      83                 :            : void
      84                 :          0 : nln_destroy(struct nln *nln)
      85                 :            : {
      86         [ #  # ]:          0 :     if (nln) {
      87         [ #  # ]:          0 :         ovs_assert(ovs_list_is_empty(&nln->all_notifiers));
      88                 :          0 :         nl_sock_destroy(nln->notify_sock);
      89                 :          0 :         free(nln);
      90                 :            :     }
      91                 :          0 : }
      92                 :            : 
      93                 :            : /* Registers 'cb' to be called with auxiliary data 'aux' with change
      94                 :            :  * notifications.  The notifier is stored in 'notifier', which the caller must
      95                 :            :  * not modify or free.
      96                 :            :  *
      97                 :            :  * This is probably not the function you want.  You should probably be using
      98                 :            :  * message specific notifiers like rtnetlink_link_notifier_register().
      99                 :            :  *
     100                 :            :  * Returns an initialized nln_notifier if successful, otherwise NULL. */
     101                 :            : struct nln_notifier *
     102                 :       2669 : nln_notifier_create(struct nln *nln, int multicast_group, nln_notify_func *cb,
     103                 :            :                     void *aux)
     104                 :            : {
     105                 :            :     struct nln_notifier *notifier;
     106                 :            :     int error;
     107                 :            : 
     108         [ +  + ]:       2669 :     if (!nln->notify_sock) {
     109                 :            :         struct nl_sock *sock;
     110                 :            : 
     111                 :       1370 :         error = nl_sock_create(nln->protocol, &sock);
     112         [ -  + ]:       1370 :         if (error) {
     113         [ #  # ]:          0 :             VLOG_WARN("could not create netlink socket: %s",
     114                 :            :                       ovs_strerror(error));
     115                 :          0 :             return NULL;
     116                 :            :         }
     117                 :       1370 :         nln->notify_sock = sock;
     118                 :            :     } else {
     119                 :            :         /* Catch up on notification work so that the new notifier won't
     120                 :            :          * receive any stale notifications. */
     121                 :       1299 :         nln_run(nln);
     122                 :            :     }
     123                 :            : 
     124                 :       2669 :     error = nl_sock_join_mcgroup(nln->notify_sock, multicast_group);
     125         [ -  + ]:       2669 :     if (error) {
     126         [ #  # ]:          0 :         VLOG_WARN("could not join netlink multicast group: %s",
     127                 :            :                   ovs_strerror(error));
     128                 :          0 :         return NULL;
     129                 :            :     }
     130                 :            : 
     131                 :       2669 :     notifier = xmalloc(sizeof *notifier);
     132                 :       2669 :     notifier->multicast_group = multicast_group;
     133                 :       2669 :     notifier->cb = cb;
     134                 :       2669 :     notifier->aux = aux;
     135                 :       2669 :     notifier->nln = nln;
     136                 :            : 
     137                 :       2669 :     ovs_list_push_back(&nln->all_notifiers, &notifier->node);
     138                 :            : 
     139                 :       2669 :     return notifier;
     140                 :            : }
     141                 :            : 
     142                 :            : /* Destroys 'notifier', which must have previously been created with
     143                 :            :  * nln_notifier_register(). */
     144                 :            : void
     145                 :        617 : nln_notifier_destroy(struct nln_notifier *notifier)
     146                 :            : {
     147         [ +  - ]:        617 :     if (notifier) {
     148                 :        617 :         struct nln *nln = notifier->nln;
     149                 :            :         struct nln_notifier *iter;
     150                 :        617 :         int count = 0;
     151                 :            : 
     152                 :        617 :         ovs_list_remove(&notifier->node);
     153                 :            : 
     154                 :            :         /* Leave the group if no other notifier is interested in it. */
     155         [ +  + ]:       1232 :         LIST_FOR_EACH (iter, node, &nln->all_notifiers) {
     156         [ +  - ]:        615 :             if (iter->multicast_group == notifier->multicast_group) {
     157                 :        615 :                 count++;
     158                 :            :             }
     159                 :            :         }
     160         [ +  + ]:        617 :         if (count == 0) {
     161                 :          2 :             nl_sock_leave_mcgroup(nln->notify_sock, notifier->multicast_group);
     162                 :            :         }
     163                 :            : 
     164         [ +  + ]:        617 :         if (ovs_list_is_empty(&nln->all_notifiers)) {
     165                 :          2 :             nl_sock_destroy(nln->notify_sock);
     166                 :          2 :             nln->notify_sock = NULL;
     167                 :            :         }
     168                 :        617 :         free(notifier);
     169                 :            :     }
     170                 :        617 : }
     171                 :            : 
     172                 :            : /* Calls all of the registered notifiers, passing along any as-yet-unreported
     173                 :            :  * change events. */
     174                 :            : void
     175                 :    1563631 : nln_run(struct nln *nln)
     176                 :            : {
     177                 :            :     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
     178                 :            : 
     179 [ +  - ][ +  + ]:    1563631 :     if (!nln->notify_sock || nln->has_run) {
     180                 :    1354318 :         return;
     181                 :            :     }
     182                 :            : 
     183                 :     209313 :     nln->has_run = true;
     184                 :            :     for (;;) {
     185                 :            :         uint64_t buf_stub[4096 / 8];
     186                 :            :         struct ofpbuf buf;
     187                 :            :         int error;
     188                 :            : 
     189                 :     212295 :         ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
     190                 :     212295 :         error = nl_sock_recv(nln->notify_sock, &buf, false);
     191         [ +  + ]:     212295 :         if (!error) {
     192                 :       2982 :             int group = nln->parse(&buf, nln->change);
     193                 :            : 
     194         [ +  - ]:       2982 :             if (group != 0) {
     195                 :       2982 :                 nln_report(nln, nln->change, group);
     196                 :            :             } else {
     197         [ #  # ]:          0 :                 VLOG_WARN_RL(&rl, "unexpected netlink message contents");
     198                 :          0 :                 nln_report(nln, NULL, 0);
     199                 :            :             }
     200                 :       2982 :             ofpbuf_uninit(&buf);
     201         [ +  - ]:     209313 :         } else if (error == EAGAIN) {
     202                 :     209313 :             return;
     203                 :            :         } else {
     204         [ #  # ]:          0 :             if (error == ENOBUFS) {
     205                 :            :                 /* The socket buffer might be full, there could be too many
     206                 :            :                  * notifications, so it makes sense to call nln_report() */
     207                 :          0 :                 nln_report(nln, NULL, 0);
     208         [ #  # ]:          0 :                 VLOG_WARN_RL(&rl, "netlink receive buffer overflowed");
     209                 :            :             } else {
     210         [ #  # ]:          0 :                 VLOG_WARN_RL(&rl, "error reading netlink socket: %s",
     211                 :            :                              ovs_strerror(error));
     212                 :            :             }
     213                 :          0 :             return;
     214                 :            :         }
     215                 :       2982 :     }
     216                 :            : }
     217                 :            : 
     218                 :            : /* Causes poll_block() to wake up when change notifications are ready. */
     219                 :            : void
     220                 :    1561482 : nln_wait(struct nln *nln)
     221                 :            : {
     222                 :    1561482 :     nln->has_run = false;
     223         [ +  - ]:    1561482 :     if (nln->notify_sock) {
     224                 :    1561482 :         nl_sock_wait(nln->notify_sock, POLLIN);
     225                 :            :     }
     226                 :    1561482 : }
     227                 :            : 
     228                 :            : static void
     229                 :       2982 : nln_report(const struct nln *nln, void *change, int group)
     230                 :            : {
     231                 :            :     struct nln_notifier *notifier;
     232                 :            : 
     233         [ +  - ]:       2982 :     if (change) {
     234                 :       2982 :         COVERAGE_INC(nln_changed);
     235                 :            :     }
     236                 :            : 
     237         [ +  + ]:       8946 :     LIST_FOR_EACH (notifier, node, &nln->all_notifiers) {
     238 [ +  - ][ +  + ]:       5964 :         if (!change || group == notifier->multicast_group) {
     239                 :       5193 :             notifier->cb(change, notifier->aux);
     240                 :            :         }
     241                 :            :     }
     242                 :       2982 : }

Generated by: LCOV version 1.12