Coverage Report

Created: 2023-03-26 07:41

/src/openvswitch/lib/netlink-notifier.c
Line
Count
Source (jump to first uncovered line)
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
VLOG_DEFINE_THIS_MODULE(netlink_notifier);
32
33
COVERAGE_DEFINE(nln_changed);
34
35
struct nln {
36
    struct nl_sock *notify_sock; /* Netlink socket. */
37
    struct ovs_list all_notifiers;   /* All nln notifiers. */
38
    bool has_run;                /* Guard for run and wait functions. */
39
40
    /* Passed in by nln_create(). */
41
    int protocol;                /* Protocol passed to nl_sock_create(). */
42
    nln_parse_func *parse;       /* Message parsing function. */
43
    void *change;                /* Change passed to parse. */
44
};
45
46
struct nln_notifier {
47
    struct ovs_list node;        /* In struct nln's 'all_notifiers' list. */
48
    struct nln *nln;             /* Parent nln. */
49
50
    int multicast_group;         /* Multicast group we listen on. */
51
    nln_notify_func *cb;
52
    void *aux;
53
};
54
55
/* Creates an nln handle which may be used to manage change notifications.  The
56
 * created handle will listen for netlink messages on 'multicast_group' using
57
 * netlink protocol 'protocol' (e.g. NETLINK_ROUTE, NETLINK_GENERIC, ...).
58
 * Incoming messages will be parsed with 'parse' which will be passed 'change'
59
 * as an argument. */
60
struct nln *
61
nln_create(int protocol, nln_parse_func *parse, void *change)
62
0
{
63
0
    struct nln *nln;
64
65
0
    nln = xzalloc(sizeof *nln);
66
0
    nln->notify_sock = NULL;
67
0
    nln->protocol = protocol;
68
0
    nln->parse = parse;
69
0
    nln->change = change;
70
0
    nln->has_run = false;
71
72
0
    ovs_list_init(&nln->all_notifiers);
73
0
    return nln;
74
0
}
75
76
/* Destroys 'nln' by freeing any memory it has reserved and closing any sockets
77
 * it has opened.
78
 *
79
 * The caller is responsible for destroying any notifiers created by this
80
 * 'nln' before destroying 'nln'. */
81
void
82
nln_destroy(struct nln *nln)
83
0
{
84
0
    if (nln) {
85
0
        ovs_assert(ovs_list_is_empty(&nln->all_notifiers));
86
0
        nl_sock_destroy(nln->notify_sock);
87
0
        free(nln);
88
0
    }
89
0
}
90
91
/* Registers 'cb' to be called with auxiliary data 'aux' with change
92
 * notifications.  The notifier is stored in 'notifier', which the caller must
93
 * not modify or free.
94
 *
95
 * This is probably not the function you want.  You should probably be using
96
 * message specific notifiers like rtnetlink_link_notifier_register().
97
 *
98
 * Returns an initialized nln_notifier if successful, otherwise NULL. */
99
struct nln_notifier *
100
nln_notifier_create(struct nln *nln, int multicast_group, nln_notify_func *cb,
101
                    void *aux)
102
0
{
103
0
    struct nln_notifier *notifier;
104
0
    int error;
105
106
0
    if (!nln->notify_sock) {
107
0
        struct nl_sock *sock;
108
109
0
        error = nl_sock_create(nln->protocol, &sock);
110
0
        if (error) {
111
0
            VLOG_WARN("could not create netlink socket: %s",
112
0
                      ovs_strerror(error));
113
0
            return NULL;
114
0
        }
115
0
        nln->notify_sock = sock;
116
0
    } else {
117
        /* Catch up on notification work so that the new notifier won't
118
         * receive any stale notifications. */
119
0
        nln_run(nln);
120
0
    }
121
122
0
    error = nl_sock_join_mcgroup(nln->notify_sock, multicast_group);
123
0
    if (error) {
124
0
        VLOG_WARN("could not join netlink multicast group: %s",
125
0
                  ovs_strerror(error));
126
0
        return NULL;
127
0
    }
128
129
0
    notifier = xmalloc(sizeof *notifier);
130
0
    notifier->multicast_group = multicast_group;
131
0
    notifier->cb = cb;
132
0
    notifier->aux = aux;
133
0
    notifier->nln = nln;
134
135
0
    ovs_list_push_back(&nln->all_notifiers, &notifier->node);
136
137
0
    return notifier;
138
0
}
139
140
/* Destroys 'notifier', which must have previously been created with
141
 * nln_notifier_register(). */
142
void
143
nln_notifier_destroy(struct nln_notifier *notifier)
144
0
{
145
0
    if (notifier) {
146
0
        struct nln *nln = notifier->nln;
147
0
        struct nln_notifier *iter;
148
0
        int count = 0;
149
150
0
        ovs_list_remove(&notifier->node);
151
152
        /* Leave the group if no other notifier is interested in it. */
153
0
        LIST_FOR_EACH (iter, node, &nln->all_notifiers) {
154
0
            if (iter->multicast_group == notifier->multicast_group) {
155
0
                count++;
156
0
            }
157
0
        }
158
0
        if (count == 0) {
159
0
            nl_sock_leave_mcgroup(nln->notify_sock, notifier->multicast_group);
160
0
        }
161
162
0
        if (ovs_list_is_empty(&nln->all_notifiers)) {
163
0
            nl_sock_destroy(nln->notify_sock);
164
0
            nln->notify_sock = NULL;
165
0
        }
166
0
        free(notifier);
167
0
    }
168
0
}
169
170
/* Calls all of the registered notifiers, passing along any as-yet-unreported
171
 * change events. */
172
void
173
nln_run(struct nln *nln)
174
0
{
175
0
    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
176
177
0
    if (!nln->notify_sock || nln->has_run) {
178
0
        return;
179
0
    }
180
181
0
    nln->has_run = true;
182
0
    for (;;) {
183
0
        uint64_t buf_stub[4096 / 8];
184
0
        struct ofpbuf buf;
185
0
        int error;
186
187
0
        ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
188
0
        error = nl_sock_recv(nln->notify_sock, &buf, NULL, false);
189
0
        if (!error) {
190
0
            int group = nln->parse(&buf, nln->change);
191
192
0
            if (group != 0) {
193
0
                nln_report(nln, nln->change, group);
194
0
            } else {
195
0
                VLOG_WARN_RL(&rl, "unexpected netlink message contents");
196
0
                nln_report(nln, NULL, 0);
197
0
            }
198
0
            ofpbuf_uninit(&buf);
199
0
        } else if (error == EAGAIN) {
200
0
            return;
201
0
        } else {
202
0
            if (error == ENOBUFS) {
203
                /* The socket buffer might be full, there could be too many
204
                 * notifications, so it makes sense to call nln_report() */
205
0
                nln_report(nln, NULL, 0);
206
0
                VLOG_WARN_RL(&rl, "netlink receive buffer overflowed");
207
0
            } else {
208
0
                VLOG_WARN_RL(&rl, "error reading netlink socket: %s",
209
0
                             ovs_strerror(error));
210
0
            }
211
0
            return;
212
0
        }
213
0
    }
214
0
}
215
216
/* Causes poll_block() to wake up when change notifications are ready. */
217
void
218
nln_wait(struct nln *nln)
219
0
{
220
0
    nln->has_run = false;
221
0
    if (nln->notify_sock) {
222
0
        nl_sock_wait(nln->notify_sock, POLLIN);
223
0
    }
224
0
}
225
226
void
227
nln_report(const struct nln *nln, void *change, int group)
228
0
{
229
0
    struct nln_notifier *notifier;
230
231
0
    if (change) {
232
0
        COVERAGE_INC(nln_changed);
233
0
    }
234
235
0
    LIST_FOR_EACH (notifier, node, &nln->all_notifiers) {
236
0
        if (!change || group == notifier->multicast_group) {
237
0
            notifier->cb(change, notifier->aux);
238
0
        }
239
0
    }
240
0
}