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, ¬ifier->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(¬ifier->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 : }
|