libnl  1.1
cache_mngr.c
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-2007 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 
89 static 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);
99 }
100 
101 static 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 
133 found:
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 handle Netlink socket/handle to be used
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  */
149 struct nl_cache_mngr *nl_cache_mngr_alloc(struct nl_handle *handle,
150  int protocol, int flags)
151 {
152  struct nl_cache_mngr *mngr;
153 
154  if (handle == NULL)
155  BUG();
156 
157  mngr = calloc(1, sizeof(*mngr));
158  if (!mngr)
159  goto enomem;
160 
161  mngr->cm_handle = handle;
162  mngr->cm_nassocs = 32;
163  mngr->cm_protocol = protocol;
164  mngr->cm_flags = flags;
165  mngr->cm_assocs = calloc(mngr->cm_nassocs,
166  sizeof(struct nl_cache_assoc));
167  if (!mngr->cm_assocs)
168  goto enomem;
169 
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_disable_sequence_check(mngr->cm_handle);
176 
177  if (nl_connect(mngr->cm_handle, protocol) < 0)
178  goto errout;
179 
180  if (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  return mngr;
187 
188 enomem:
189  nl_errno(ENOMEM);
190 errout:
191  nl_cache_mngr_free(mngr);
192  return NULL;
193 }
194 
195 /**
196  * Add cache responsibility to cache manager
197  * @arg mngr Cache manager.
198  * @arg name Name of cache to keep track of
199  * @arg cb Function to be called upon changes.
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 The newly allocated cache or NULL on failure.
208  */
209 struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
210  change_func_t cb)
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  nl_error(ENOENT, "Unknown cache type");
220  return NULL;
221  }
222 
223  if (ops->co_protocol != mngr->cm_protocol) {
224  nl_error(EINVAL, "Netlink protocol mismatch");
225  return NULL;
226  }
227 
228  if (ops->co_groups == NULL) {
229  nl_error(EOPNOTSUPP, NULL);
230  return NULL;
231  }
232 
233  for (i = 0; i < mngr->cm_nassocs; i++) {
234  if (mngr->cm_assocs[i].ca_cache &&
235  mngr->cm_assocs[i].ca_cache->c_ops == ops) {
236  nl_error(EEXIST, "Cache of this type already managed");
237  return NULL;
238  }
239  }
240 
241 retry:
242  for (i = 0; i < mngr->cm_nassocs; i++)
243  if (!mngr->cm_assocs[i].ca_cache)
244  break;
245 
246  if (i >= mngr->cm_nassocs) {
247  mngr->cm_nassocs += 16;
248  mngr->cm_assocs = realloc(mngr->cm_assocs,
249  mngr->cm_nassocs *
250  sizeof(struct nl_cache_assoc));
251  if (mngr->cm_assocs == NULL) {
252  nl_errno(ENOMEM);
253  return NULL;
254  } else {
255  NL_DBG(1, "Increased capacity of cache manager %p " \
256  "to %d\n", mngr, mngr->cm_nassocs);
257  goto retry;
258  }
259  }
260 
261  cache = nl_cache_alloc(ops);
262  if (!cache) {
263  nl_errno(ENOMEM);
264  return NULL;
265  }
266 
267  for (grp = ops->co_groups; grp->ag_group; grp++) {
268  err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
269  if (err < 0)
270  goto errout_free_cache;
271  }
272 
273  err = nl_cache_refill(mngr->cm_handle, cache);
274  if (err < 0)
275  goto errout_drop_membership;
276 
277  mngr->cm_assocs[i].ca_cache = cache;
278  mngr->cm_assocs[i].ca_change = cb;
279 
280  if (mngr->cm_flags & NL_AUTO_PROVIDE)
281  nl_cache_mngt_provide(cache);
282 
283  NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
284  cache, nl_cache_name(cache), mngr);
285 
286  return cache;
287 
288 errout_drop_membership:
289  for (grp = ops->co_groups; grp->ag_group; grp++)
290  nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
291 errout_free_cache:
292  nl_cache_free(cache);
293 
294  return NULL;
295 }
296 
297 /**
298  * Get file descriptor
299  * @arg mngr Cache Manager
300  *
301  * Get the file descriptor of the socket associated to the manager.
302  * This can be used to change socket options or monitor activity
303  * using poll()/select().
304  */
305 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
306 {
307  return nl_socket_get_fd(mngr->cm_handle);
308 }
309 
310 /**
311  * Check for event notifications
312  * @arg mngr Cache Manager
313  * @arg timeout Upper limit poll() will block, in milliseconds.
314  *
315  * Causes poll() to be called to check for new event notifications
316  * being available. Automatically receives and handles available
317  * notifications.
318  *
319  * This functionally is ideally called regularly during an idle
320  * period.
321  *
322  * @return A positive value if at least one update was handled, 0
323  * for none, or a negative error code.
324  */
325 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
326 {
327  int ret;
328  struct pollfd fds = {
329  .fd = nl_socket_get_fd(mngr->cm_handle),
330  .events = POLLIN,
331  };
332 
333  NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
334  ret = poll(&fds, 1, timeout);
335  NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
336  if (ret < 0)
337  return nl_errno(errno);
338 
339  if (ret == 0)
340  return 0;
341 
342  return nl_cache_mngr_data_ready(mngr);
343 }
344 
345 /**
346  * Receive available event notifications
347  * @arg mngr Cache manager
348  *
349  * This function can be called if the socket associated to the manager
350  * contains updates to be received. This function should not be used
351  * if nl_cache_mngr_poll() is used.
352  *
353  * @return A positive value if at least one update was handled, 0
354  * for none, or a negative error code.
355  */
356 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
357 {
358  int err;
359 
360  err = nl_recvmsgs_default(mngr->cm_handle);
361  if (err < 0)
362  return err;
363 
364  return 1;
365 }
366 
367 /**
368  * Free cache manager
369  * @arg mngr Cache manager
370  *
371  * Release all resources after usage of a cache manager.
372  */
373 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
374 {
375  if (!mngr)
376  return;
377 
378  if (mngr->cm_handle) {
379  nl_close(mngr->cm_handle);
380  nl_handle_destroy(mngr->cm_handle);
381  }
382 
383  free(mngr->cm_assocs);
384  free(mngr);
385 
386  NL_DBG(1, "Cache manager %p freed\n", mngr);
387 }
388 
389 /** @} */
void nl_cache_mngt_provide(struct nl_cache *cache)
Provide a cache for global use.
Definition: cache_mngt.c:189
struct nl_cache * nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, change_func_t cb)
Add cache responsibility to cache manager.
Definition: cache_mngr.c:209
uint16_t nlmsg_type
Message type (content type)
void nl_disable_sequence_check(struct nl_handle *handle)
Disable sequence number checking.
Definition: socket.c:267
int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
Get file descriptor.
Definition: cache_mngr.c:305
void nl_handle_destroy(struct nl_handle *handle)
Destroy netlink handle.
Definition: socket.c:228
Customized handler specified by the user.
Definition: handlers.h:83
int nl_recvmsgs_default(struct nl_handle *handle)
Receive a set of message from a netlink socket using handlers in nl_handle.
Definition: nl.c:781
int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
Check for event notifications.
Definition: cache_mngr.c:325
int nl_socket_set_nonblocking(struct nl_handle *handle)
Set file descriptor of socket handle to non-blocking state.
Definition: socket.c:436
struct nl_cache_ops * nl_cache_ops_lookup(const char *name)
Lookup the set cache operations of a certain cache type.
Definition: cache_mngt.c:36
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition: cache.c:265
int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition: cache.c:662
void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
Dump this object according to the specified parameters.
Definition: object.c:246
void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
Dump message in human readable format to file descriptor.
Definition: msg.c:950
Skip this message.
Definition: handlers.h:62
Cache Operations.
Definition: cache-api.h:163
struct nlmsghdr * nlmsg_hdr(struct nl_msg *n)
Return actual netlink message.
Definition: msg.c:646
int ag_group
Netlink group identifier.
Definition: cache-api.h:149
Message is valid.
Definition: handlers.h:95
int nl_connect(struct nl_handle *handle, int protocol)
Create and connect netlink socket.
Definition: nl.c:191
int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
Receive available event notifications.
Definition: cache_mngr.c:356
struct nl_cache_mngr * nl_cache_mngr_alloc(struct nl_handle *handle, int protocol, int flags)
Allocate new cache manager.
Definition: cache_mngr.c:149
int nl_socket_drop_membership(struct nl_handle *handle, int group)
Leave a group.
Definition: socket.c:370
int mt_id
Netlink message type.
Definition: cache-api.h:131
void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
Free cache manager.
Definition: cache_mngr.c:373
int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg)
Modify the callback handler associated to the socket.
Definition: socket.c:493
int nl_socket_add_membership(struct nl_handle *handle, int group)
Join a group.
Definition: socket.c:343
void nl_close(struct nl_handle *handle)
Close/Disconnect netlink socket.
Definition: nl.c:247
Address family to netlink group association.
Definition: cache-api.h:143
int nl_debug
Debug level.
Definition: utils.c:25
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate an empty cache.
Definition: cache.c:173