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