1/*
2 * lib/cache_mngr.c	Cache Manager
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup cache_mngt
14 * @defgroup cache_mngr Manager
15 * @brief Helps keeping caches up to date.
16 *
17 * The purpose of a cache manager is to keep track of caches and
18 * automatically receive event notifications to keep the caches
19 * up to date with the kernel state. Each manager has exactly one
20 * netlink socket assigned which limits the scope of each manager
21 * to exactly one netlink family. Therefore all caches committed
22 * to a manager must be part of the same netlink family. Due to the
23 * nature of a manager, it is not possible to have a cache maintain
24 * two instances of the same cache type. The socket is subscribed
25 * to the event notification group of each cache and also put into
26 * non-blocking mode. Functions exist to poll() on the socket to
27 * wait for new events to be received.
28 *
29 * @code
30 * App       libnl                        Kernel
31 *        |                            |
32 *            +-----------------+        [ notification, link change ]
33 *        |   |  Cache Manager  |      | [   (IFF_UP | IFF_RUNNING)  ]
34 *            |                 |                |
35 *        |   |   +------------+|      |         |  [ notification, new addr ]
36 *    <-------|---| route/link |<-------(async)--+  [  10.0.1.1/32 dev eth1  ]
37 *        |   |   +------------+|      |                      |
38 *            |   +------------+|                             |
39 *    <---|---|---| route/addr |<------|-(async)--------------+
40 *            |   +------------+|
41 *        |   |   +------------+|      |
42 *    <-------|---| ...        ||
43 *        |   |   +------------+|      |
44 *            +-----------------+
45 *        |                            |
46 * @endcode
47 *
48 * @par 1) Creating a new cache manager
49 * @code
50 * struct nl_cache_mngr *mngr;
51 *
52 * // Allocate a new cache manager for RTNETLINK and automatically
53 * // provide the caches added to the manager.
54 * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE);
55 * @endcode
56 *
57 * @par 2) Keep track of a cache
58 * @code
59 * struct nl_cache *cache;
60 *
61 * // Create a new cache for links/interfaces and ask the manager to
62 * // keep it up to date for us. This will trigger a full dump request
63 * // to initially fill the cache.
64 * cache = nl_cache_mngr_add(mngr, "route/link");
65 * @endcode
66 *
67 * @par 3) Make the manager receive updates
68 * @code
69 * // Give the manager the ability to receive updates, will call poll()
70 * // with a timeout of 5 seconds.
71 * if (nl_cache_mngr_poll(mngr, 5000) > 0) {
72 *         // Manager received at least one update, dump cache?
73 *         nl_cache_dump(cache, ...);
74 * }
75 * @endcode
76 *
77 * @par 4) Release cache manager
78 * @code
79 * nl_cache_mngr_free(mngr);
80 * @endcode
81 * @{
82 */
83
84#include <netlink-local.h>
85#include <netlink/netlink.h>
86#include <netlink/cache.h>
87#include <netlink/utils.h>
88
89static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
90{
91	struct nl_cache_assoc *ca = p->pp_arg;
92
93	NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
94#ifdef NL_DEBUG
95	if (nl_debug >= 4)
96		nl_object_dump(obj, &nl_debug_dp);
97#endif
98	return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
99}
100
101static int event_input(struct nl_msg *msg, void *arg)
102{
103	struct nl_cache_mngr *mngr = arg;
104	int protocol = nlmsg_get_proto(msg);
105	int type = nlmsg_hdr(msg)->nlmsg_type;
106	struct nl_cache_ops *ops;
107	int i, n;
108	struct nl_parser_param p = {
109		.pp_cb = include_cb,
110	};
111
112	NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
113	       mngr, msg);
114#ifdef NL_DEBUG
115	if (nl_debug >= 4)
116		nl_msg_dump(msg, stderr);
117#endif
118
119	if (mngr->cm_protocol != protocol)
120		BUG();
121
122	for (i = 0; i < mngr->cm_nassocs; i++) {
123		if (mngr->cm_assocs[i].ca_cache) {
124			ops = mngr->cm_assocs[i].ca_cache->c_ops;
125			for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
126				if (ops->co_msgtypes[n].mt_id == type)
127					goto found;
128		}
129	}
130
131	return NL_SKIP;
132
133found:
134	NL_DBG(2, "Associated message %p to cache %p\n",
135	       msg, mngr->cm_assocs[i].ca_cache);
136	p.pp_arg = &mngr->cm_assocs[i];
137
138	return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
139}
140
141/**
142 * Allocate new cache manager
143 * @arg sk		Netlink socket.
144 * @arg protocol	Netlink Protocol this manager is used for
145 * @arg flags		Flags
146 *
147 * @return Newly allocated cache manager or NULL on failure.
148 */
149int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
150			struct nl_cache_mngr **result)
151{
152	struct nl_cache_mngr *mngr;
153	int err = -NLE_NOMEM;
154
155	if (sk == NULL)
156		BUG();
157
158	mngr = calloc(1, sizeof(*mngr));
159	if (!mngr)
160		goto errout;
161
162	mngr->cm_handle = sk;
163	mngr->cm_nassocs = 32;
164	mngr->cm_protocol = protocol;
165	mngr->cm_flags = flags;
166	mngr->cm_assocs = calloc(mngr->cm_nassocs,
167				 sizeof(struct nl_cache_assoc));
168	if (!mngr->cm_assocs)
169		goto errout;
170
171	nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
172			    event_input, mngr);
173
174	/* Required to receive async event notifications */
175	nl_socket_disable_seq_check(mngr->cm_handle);
176
177	if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
178		goto errout;
179
180	if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
181		goto errout;
182
183	NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
184	       mngr, protocol, mngr->cm_nassocs);
185
186	*result = mngr;
187	return 0;
188
189errout:
190	nl_cache_mngr_free(mngr);
191	return err;
192}
193
194/**
195 * Add cache responsibility to cache manager
196 * @arg mngr		Cache manager.
197 * @arg name		Name of cache to keep track of
198 * @arg cb		Function to be called upon changes.
199 * @arg result		Pointer to store added cache.
200 *
201 * Allocates a new cache of the specified type and adds it to the manager.
202 * The operation will trigger a full dump request from the kernel to
203 * initially fill the contents of the cache. The manager will subscribe
204 * to the notification group of the cache to keep track of any further
205 * changes.
206 *
207 * @return 0 on success or a negative error code.
208 */
209int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
210		      change_func_t cb, void *data, struct nl_cache **result)
211{
212	struct nl_cache_ops *ops;
213	struct nl_cache *cache;
214	struct nl_af_group *grp;
215	int err, i;
216
217	ops = nl_cache_ops_lookup(name);
218	if (!ops)
219		return -NLE_NOCACHE;
220
221	if (ops->co_protocol != mngr->cm_protocol)
222		return -NLE_PROTO_MISMATCH;
223
224	if (ops->co_groups == NULL)
225		return -NLE_OPNOTSUPP;
226
227	for (i = 0; i < mngr->cm_nassocs; i++)
228		if (mngr->cm_assocs[i].ca_cache &&
229		    mngr->cm_assocs[i].ca_cache->c_ops == ops)
230			return -NLE_EXIST;
231
232retry:
233	for (i = 0; i < mngr->cm_nassocs; i++)
234		if (!mngr->cm_assocs[i].ca_cache)
235			break;
236
237	if (i >= mngr->cm_nassocs) {
238		mngr->cm_nassocs += 16;
239		mngr->cm_assocs = realloc(mngr->cm_assocs,
240					  mngr->cm_nassocs *
241					  sizeof(struct nl_cache_assoc));
242		if (mngr->cm_assocs == NULL)
243			return -NLE_NOMEM;
244		else {
245			NL_DBG(1, "Increased capacity of cache manager %p " \
246				  "to %d\n", mngr, mngr->cm_nassocs);
247			goto retry;
248		}
249	}
250
251	cache = nl_cache_alloc(ops);
252	if (!cache)
253		return -NLE_NOMEM;
254
255	for (grp = ops->co_groups; grp->ag_group; grp++) {
256		err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
257		if (err < 0)
258			goto errout_free_cache;
259	}
260
261	err = nl_cache_refill(mngr->cm_handle, cache);
262	if (err < 0)
263		goto errout_drop_membership;
264
265	mngr->cm_assocs[i].ca_cache = cache;
266	mngr->cm_assocs[i].ca_change = cb;
267	mngr->cm_assocs[i].ca_change_data = data;
268
269	if (mngr->cm_flags & NL_AUTO_PROVIDE)
270		nl_cache_mngt_provide(cache);
271
272	NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
273	       cache, nl_cache_name(cache), mngr);
274
275	*result = cache;
276	return 0;
277
278errout_drop_membership:
279	for (grp = ops->co_groups; grp->ag_group; grp++)
280		nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
281errout_free_cache:
282	nl_cache_free(cache);
283
284	return err;
285}
286
287/**
288 * Get file descriptor
289 * @arg mngr		Cache Manager
290 *
291 * Get the file descriptor of the socket associated to the manager.
292 * This can be used to change socket options or monitor activity
293 * using poll()/select().
294 */
295int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
296{
297	return nl_socket_get_fd(mngr->cm_handle);
298}
299
300/**
301 * Check for event notifications
302 * @arg mngr		Cache Manager
303 * @arg timeout		Upper limit poll() will block, in milliseconds.
304 *
305 * Causes poll() to be called to check for new event notifications
306 * being available. Automatically receives and handles available
307 * notifications.
308 *
309 * This functionally is ideally called regularly during an idle
310 * period.
311 *
312 * @return A positive value if at least one update was handled, 0
313 *         for none, or a  negative error code.
314 */
315int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
316{
317	int ret;
318	struct pollfd fds = {
319		.fd = nl_socket_get_fd(mngr->cm_handle),
320		.events = POLLIN,
321	};
322
323	NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
324	ret = poll(&fds, 1, timeout);
325	NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
326	if (ret < 0)
327		return -nl_syserr2nlerr(errno);
328
329	if (ret == 0)
330		return 0;
331
332	return nl_cache_mngr_data_ready(mngr);
333}
334
335/**
336 * Receive available event notifications
337 * @arg mngr		Cache manager
338 *
339 * This function can be called if the socket associated to the manager
340 * contains updates to be received. This function should not be used
341 * if nl_cache_mngr_poll() is used.
342 *
343 * @return A positive value if at least one update was handled, 0
344 *         for none, or a  negative error code.
345 */
346int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
347{
348	int err;
349
350	err = nl_recvmsgs_default(mngr->cm_handle);
351	if (err < 0)
352		return err;
353
354	return 1;
355}
356
357/**
358 * Free cache manager and all caches.
359 * @arg mngr		Cache manager.
360 *
361 * Release all resources after usage of a cache manager.
362 */
363void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
364{
365	int i;
366
367	if (!mngr)
368		return;
369
370	if (mngr->cm_handle)
371		nl_close(mngr->cm_handle);
372
373	for (i = 0; i < mngr->cm_nassocs; i++)
374		if (mngr->cm_assocs[i].ca_cache)
375			nl_cache_free(mngr->cm_assocs[i].ca_cache);
376
377	free(mngr->cm_assocs);
378	free(mngr);
379
380	NL_DBG(1, "Cache manager %p freed\n", mngr);
381}
382
383/** @} */
384