libnl
3.2.3
|
00001 /* 00002 * lib/cache_mngr.c Cache Manager 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup cache_mngt 00014 * @defgroup cache_mngr Manager 00015 * @brief Helps keeping caches up to date. 00016 * 00017 * The purpose of a cache manager is to keep track of caches and 00018 * automatically receive event notifications to keep the caches 00019 * up to date with the kernel state. Each manager has exactly one 00020 * netlink socket assigned which limits the scope of each manager 00021 * to exactly one netlink family. Therefore all caches committed 00022 * to a manager must be part of the same netlink family. Due to the 00023 * nature of a manager, it is not possible to have a cache maintain 00024 * two instances of the same cache type. The socket is subscribed 00025 * to the event notification group of each cache and also put into 00026 * non-blocking mode. Functions exist to poll() on the socket to 00027 * wait for new events to be received. 00028 * 00029 * @code 00030 * App libnl Kernel 00031 * | | 00032 * +-----------------+ [ notification, link change ] 00033 * | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ] 00034 * | | | 00035 * | | +------------+| | | [ notification, new addr ] 00036 * <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ] 00037 * | | +------------+| | | 00038 * | +------------+| | 00039 * <---|---|---| route/addr |<------|-(async)--------------+ 00040 * | +------------+| 00041 * | | +------------+| | 00042 * <-------|---| ... || 00043 * | | +------------+| | 00044 * +-----------------+ 00045 * | | 00046 * @endcode 00047 * 00048 * @par 1) Creating a new cache manager 00049 * @code 00050 * struct nl_cache_mngr *mngr; 00051 * 00052 * // Allocate a new cache manager for RTNETLINK and automatically 00053 * // provide the caches added to the manager. 00054 * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE); 00055 * @endcode 00056 * 00057 * @par 2) Keep track of a cache 00058 * @code 00059 * struct nl_cache *cache; 00060 * 00061 * // Create a new cache for links/interfaces and ask the manager to 00062 * // keep it up to date for us. This will trigger a full dump request 00063 * // to initially fill the cache. 00064 * cache = nl_cache_mngr_add(mngr, "route/link"); 00065 * @endcode 00066 * 00067 * @par 3) Make the manager receive updates 00068 * @code 00069 * // Give the manager the ability to receive updates, will call poll() 00070 * // with a timeout of 5 seconds. 00071 * if (nl_cache_mngr_poll(mngr, 5000) > 0) { 00072 * // Manager received at least one update, dump cache? 00073 * nl_cache_dump(cache, ...); 00074 * } 00075 * @endcode 00076 * 00077 * @par 4) Release cache manager 00078 * @code 00079 * nl_cache_mngr_free(mngr); 00080 * @endcode 00081 * @{ 00082 */ 00083 00084 #include <netlink-local.h> 00085 #include <netlink/netlink.h> 00086 #include <netlink/cache.h> 00087 #include <netlink/utils.h> 00088 00089 static int include_cb(struct nl_object *obj, struct nl_parser_param *p) 00090 { 00091 struct nl_cache_assoc *ca = p->pp_arg; 00092 struct nl_cache_ops *ops = ca->ca_cache->c_ops; 00093 00094 NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); 00095 #ifdef NL_DEBUG 00096 if (nl_debug >= 4) 00097 nl_object_dump(obj, &nl_debug_dp); 00098 #endif 00099 00100 if (ops->co_event_filter) 00101 if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK) 00102 return 0; 00103 00104 return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); 00105 } 00106 00107 static int event_input(struct nl_msg *msg, void *arg) 00108 { 00109 struct nl_cache_mngr *mngr = arg; 00110 int protocol = nlmsg_get_proto(msg); 00111 int type = nlmsg_hdr(msg)->nlmsg_type; 00112 struct nl_cache_ops *ops; 00113 int i, n; 00114 struct nl_parser_param p = { 00115 .pp_cb = include_cb, 00116 }; 00117 00118 NL_DBG(2, "Cache manager %p, handling new message %p as event\n", 00119 mngr, msg); 00120 #ifdef NL_DEBUG 00121 if (nl_debug >= 4) 00122 nl_msg_dump(msg, stderr); 00123 #endif 00124 00125 if (mngr->cm_protocol != protocol) 00126 BUG(); 00127 00128 for (i = 0; i < mngr->cm_nassocs; i++) { 00129 if (mngr->cm_assocs[i].ca_cache) { 00130 ops = mngr->cm_assocs[i].ca_cache->c_ops; 00131 for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) 00132 if (ops->co_msgtypes[n].mt_id == type) 00133 goto found; 00134 } 00135 } 00136 00137 return NL_SKIP; 00138 00139 found: 00140 NL_DBG(2, "Associated message %p to cache %p\n", 00141 msg, mngr->cm_assocs[i].ca_cache); 00142 p.pp_arg = &mngr->cm_assocs[i]; 00143 00144 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); 00145 } 00146 00147 /** 00148 * Allocate new cache manager 00149 * @arg sk Netlink socket. 00150 * @arg protocol Netlink Protocol this manager is used for 00151 * @arg flags Flags 00152 * @arg result Result pointer 00153 * 00154 * @return 0 on success or a negative error code. 00155 */ 00156 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, 00157 struct nl_cache_mngr **result) 00158 { 00159 struct nl_cache_mngr *mngr; 00160 int err = -NLE_NOMEM; 00161 00162 if (sk == NULL) 00163 BUG(); 00164 00165 mngr = calloc(1, sizeof(*mngr)); 00166 if (!mngr) 00167 goto errout; 00168 00169 mngr->cm_handle = sk; 00170 mngr->cm_nassocs = 32; 00171 mngr->cm_protocol = protocol; 00172 mngr->cm_flags = flags; 00173 mngr->cm_assocs = calloc(mngr->cm_nassocs, 00174 sizeof(struct nl_cache_assoc)); 00175 if (!mngr->cm_assocs) 00176 goto errout; 00177 00178 nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM, 00179 event_input, mngr); 00180 00181 /* Required to receive async event notifications */ 00182 nl_socket_disable_seq_check(mngr->cm_handle); 00183 00184 if ((err = nl_connect(mngr->cm_handle, protocol) < 0)) 00185 goto errout; 00186 00187 if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0)) 00188 goto errout; 00189 00190 NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", 00191 mngr, protocol, mngr->cm_nassocs); 00192 00193 *result = mngr; 00194 return 0; 00195 00196 errout: 00197 nl_cache_mngr_free(mngr); 00198 return err; 00199 } 00200 00201 /** 00202 * Add cache responsibility to cache manager 00203 * @arg mngr Cache manager. 00204 * @arg name Name of cache to keep track of 00205 * @arg cb Function to be called upon changes. 00206 * @arg data Argument passed on to change callback 00207 * @arg result Pointer to store added cache. 00208 * 00209 * Allocates a new cache of the specified type and adds it to the manager. 00210 * The operation will trigger a full dump request from the kernel to 00211 * initially fill the contents of the cache. The manager will subscribe 00212 * to the notification group of the cache to keep track of any further 00213 * changes. 00214 * 00215 * @return 0 on success or a negative error code. 00216 */ 00217 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, 00218 change_func_t cb, void *data, struct nl_cache **result) 00219 { 00220 struct nl_cache_ops *ops; 00221 struct nl_cache *cache; 00222 struct nl_af_group *grp; 00223 int err, i; 00224 00225 ops = nl_cache_ops_lookup(name); 00226 if (!ops) 00227 return -NLE_NOCACHE; 00228 00229 if (ops->co_protocol != mngr->cm_protocol) 00230 return -NLE_PROTO_MISMATCH; 00231 00232 if (ops->co_groups == NULL) 00233 return -NLE_OPNOTSUPP; 00234 00235 for (i = 0; i < mngr->cm_nassocs; i++) 00236 if (mngr->cm_assocs[i].ca_cache && 00237 mngr->cm_assocs[i].ca_cache->c_ops == ops) 00238 return -NLE_EXIST; 00239 00240 retry: 00241 for (i = 0; i < mngr->cm_nassocs; i++) 00242 if (!mngr->cm_assocs[i].ca_cache) 00243 break; 00244 00245 if (i >= mngr->cm_nassocs) { 00246 mngr->cm_nassocs += 16; 00247 mngr->cm_assocs = realloc(mngr->cm_assocs, 00248 mngr->cm_nassocs * 00249 sizeof(struct nl_cache_assoc)); 00250 if (mngr->cm_assocs == NULL) 00251 return -NLE_NOMEM; 00252 else { 00253 NL_DBG(1, "Increased capacity of cache manager %p " \ 00254 "to %d\n", mngr, mngr->cm_nassocs); 00255 goto retry; 00256 } 00257 } 00258 00259 cache = nl_cache_alloc(ops); 00260 if (!cache) 00261 return -NLE_NOMEM; 00262 00263 for (grp = ops->co_groups; grp->ag_group; grp++) { 00264 err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group); 00265 if (err < 0) 00266 goto errout_free_cache; 00267 } 00268 00269 err = nl_cache_refill(mngr->cm_handle, cache); 00270 if (err < 0) 00271 goto errout_drop_membership; 00272 00273 mngr->cm_assocs[i].ca_cache = cache; 00274 mngr->cm_assocs[i].ca_change = cb; 00275 mngr->cm_assocs[i].ca_change_data = data; 00276 00277 if (mngr->cm_flags & NL_AUTO_PROVIDE) 00278 nl_cache_mngt_provide(cache); 00279 00280 NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", 00281 cache, nl_cache_name(cache), mngr); 00282 00283 *result = cache; 00284 return 0; 00285 00286 errout_drop_membership: 00287 for (grp = ops->co_groups; grp->ag_group; grp++) 00288 nl_socket_drop_membership(mngr->cm_handle, grp->ag_group); 00289 errout_free_cache: 00290 nl_cache_free(cache); 00291 00292 return err; 00293 } 00294 00295 /** 00296 * Get file descriptor 00297 * @arg mngr Cache Manager 00298 * 00299 * Get the file descriptor of the socket associated to the manager. 00300 * This can be used to change socket options or monitor activity 00301 * using poll()/select(). 00302 */ 00303 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) 00304 { 00305 return nl_socket_get_fd(mngr->cm_handle); 00306 } 00307 00308 /** 00309 * Check for event notifications 00310 * @arg mngr Cache Manager 00311 * @arg timeout Upper limit poll() will block, in milliseconds. 00312 * 00313 * Causes poll() to be called to check for new event notifications 00314 * being available. Automatically receives and handles available 00315 * notifications. 00316 * 00317 * This functionally is ideally called regularly during an idle 00318 * period. 00319 * 00320 * @return A positive value if at least one update was handled, 0 00321 * for none, or a negative error code. 00322 */ 00323 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) 00324 { 00325 int ret; 00326 struct pollfd fds = { 00327 .fd = nl_socket_get_fd(mngr->cm_handle), 00328 .events = POLLIN, 00329 }; 00330 00331 NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); 00332 ret = poll(&fds, 1, timeout); 00333 NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); 00334 if (ret < 0) 00335 return -nl_syserr2nlerr(errno); 00336 00337 if (ret == 0) 00338 return 0; 00339 00340 return nl_cache_mngr_data_ready(mngr); 00341 } 00342 00343 /** 00344 * Receive available event notifications 00345 * @arg mngr Cache manager 00346 * 00347 * This function can be called if the socket associated to the manager 00348 * contains updates to be received. This function should not be used 00349 * if nl_cache_mngr_poll() is used. 00350 * 00351 * @return A positive value if at least one update was handled, 0 00352 * for none, or a negative error code. 00353 */ 00354 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) 00355 { 00356 int err; 00357 00358 err = nl_recvmsgs_default(mngr->cm_handle); 00359 if (err < 0) 00360 return err; 00361 00362 return 1; 00363 } 00364 00365 /** 00366 * Free cache manager and all caches. 00367 * @arg mngr Cache manager. 00368 * 00369 * Release all resources after usage of a cache manager. 00370 */ 00371 void nl_cache_mngr_free(struct nl_cache_mngr *mngr) 00372 { 00373 int i; 00374 00375 if (!mngr) 00376 return; 00377 00378 if (mngr->cm_handle) 00379 nl_close(mngr->cm_handle); 00380 00381 for (i = 0; i < mngr->cm_nassocs; i++) { 00382 if (mngr->cm_assocs[i].ca_cache) { 00383 nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache); 00384 nl_cache_free(mngr->cm_assocs[i].ca_cache); 00385 } 00386 } 00387 00388 free(mngr->cm_assocs); 00389 free(mngr); 00390 00391 NL_DBG(1, "Cache manager %p freed\n", mngr); 00392 } 00393 00394 /** @} */